[
  {
    "path": ".clang-format",
    "content": "---\nBasedOnStyle: LLVM\nIndentWidth: 2\n---\nLanguage: Cpp\nAlignConsecutiveBitFields: true\nAlignEscapedNewlines: Left\nAlwaysBreakTemplateDeclarations: Yes\nIncludeCategories:\n  - Regex:           '^\"(llvm|catch2|spdlog)/'\n    Priority:        5\n    SortPriority:    0\n  - Regex:           '^\"QBDI'\n    Priority:        3\n    SortPriority:    0\n  - Regex:           '^\"(Engine|ExecBlock|ExecBroker|Patch|Utility)/'\n    Priority:        4\n    SortPriority:    0\n  - Regex:           '^<'\n    Priority:        1\n    SortPriority:    0\n  - Regex:           '.*'\n    Priority:        2\n    SortPriority:    0\nIndentCaseLabels: true\nAlwaysBreakBeforeMultilineStrings: true\n---\n\n"
  },
  {
    "path": ".clang-format.exclude-list.txt",
    "content": "cmake/llvm/.*\\.patch\\.txt\ncmake/config/ios.toolchain.cmake\n"
  },
  {
    "path": ".cmake_format.conf.py",
    "content": "with section(\"format\"):\n\n  line_width = 80\n  tab_size = 2\n  use_tabchars = False\n\nwith section(\"markup\"):\n  enable_markup = False\n\nadditional_commands = {\n  \"FetchContent_Declare\": {\n    \"pargs\": 1,\n    \"flags\": [],\n    \"kwargs\": {\n      \"URL\": '1',\n      \"URL_HASH\": '1',\n      \"GIT_REPOSITORY\": '1',\n      \"GIT_PROGRESS\": '1',\n      \"GIT_TAG\": '1',\n      \"DOWNLOAD_DIR\": '1',\n    }\n  }\n}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/assistance_request.md",
    "content": "---\nname: Assistance Request\nabout: Ask for help for using QBDI\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n**Description of the problem**\nA clear description of what your problem is.\n\n**Compilation or execution log**\nAny relevant log that illustrate your problem.\nPlease use [distinct block]https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code) to format this section.\n\n**QBDI/PyQBDI/Frida script**\nInsert your script/code that use QBDI here.\n\n**Code of the target of the instrumentation**\nInsert the script/code that you try to instrument with QBDI.\nIf the instrumented code is a public library/binary, you may provide a link to download it.\n\n**Specific command to reproduce your problem on other laptop**\nList all the command (if possible shell) that should be run the get the same output as you.\n\n**Information about the environment**\nVersion of your environment and any tools that is needed to reproduce your problem\n\nIf you use multiples devices (like a computer and a smartphone), please provide the information for each of them.\n\n- OS: (with version) [e.g. Windows 11, Debian 12.4, Android 14, ...]\n- Architecture: [e.g. X86, X64, AARCH64, ARM32]\n- QBDI version (returned by getVersion) and commit hash (if not a release version)\n- Frida version on the Host and on the target (in case of android device) (for frida/QBDI issue)\n- Python version (for PyQBDI issue)\n\n**Additional context or informations**\nAny other context about the problem here.\n\nThanks for taking the time to fill out all information! This will help us to reproduce your issue on our side.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/assistance_request_new.yml",
    "content": "name: Assistance Request (new)\ndescription: Ask for help for using QBDI\nbody:\n  - type: markdown\n    attributes:\n      label: Description of the problem\n      value: |\n        A clear description of what your problem is.\n  - type: markdown\n    attributes:\n      label: Compilation or execution log\n      value: |\n        Any relevant log that illustrate your problem.\n        Please use [distinct block]https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code) to format this section.\n  - type: markdown\n    attributes:\n      label: QBDI/PyQBDI/Frida script\n      value: |\n        Insert your script/code that use QBDI here.\n  - type: markdown\n    attributes:\n      label: Code of the target of the instrumentation\n      value: |\n        Insert the script/code that you try to instrument with QBDI.\n        If the instrumented code is a public library/binary, you may provide a link to download it.\n  - type: markdown\n    attributes:\n      label: Specific command to reproduce your problem on other laptop\n      value: |\n        List all the command (if possible shell) that should be run the get the same output as you.\n  - type: markdown\n    attributes:\n      label: Information about the environment\n      value: |\n        Version of your environment and any tools that is needed to reproduce your problem\n        \n        If you use multiples devices (like a computer and a smartphone), please provide the information for each of them.\n\n        - OS: (with version) [e.g. Windows 11, Debian 12.4, Android 14, ...] \n        - Architecture: [e.g. X86, X64, AARCH64, ARM32]\n        - QBDI version (returned by getVersion) and commit hash (if not a release version)\n        - Frida version on the Host and on the target (in case of android device) (for frida/QBDI issue)\n        - Python version (for PyQBDI issue)\n  - type: markdown\n    attributes:\n      label: Additional context or informations\n      value: |\n        Any other context about the problem here.\n\n        Thanks for taking the time to fill out all information! This will help us to reproduce your issue on our side.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a bug report\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n** Description of the problem **\nA clear description of what the bug is, including the expected and actual behavior.\n\n\n**Compilation or execution log**\nAny relevant log that may help to understand the bug.\nPlease use [distinct block]https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code) to format this section.\n\n**Minimal code/script to reproduce the problem**\nInsert your script/code that use QBDI here.\n\n**Code of the target of the instrumentation**\nInsert your script/code that you try to instrument with QBDI.\nIf the instrumented code is a public library/binary, you may provide a link to download it.\n\n**Specific command to reproduce your issue**\n  Can you list all the command (if possible shell) that should be run to reproduce the bug.\n\n**Information about the environment**\nVersion of your environment and any tools that is needed to reproduce the bug\nIf you use multiples devices (like a computer and a smartphone), please provide the information for each of them.\n\n- OS: (with version) [e.g. Windows 11, Debian 12.4, Android 14, ...]\n- Architecture: [e.g. X86, X64, AARCH64, ARM32]\n- QBDI version (returned by getVersion) and commit hash (if not a release version)\n- Frida version on the Host and on the target (in case of android device) (for frida/QBDI issue)\n- Python version (for PyQBDI issue)\n\n**Additional context or informations**\nAny other context about the problem here.\n\nThanks for taking the time to fill out this report!\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report_new.yml",
    "content": "name: Bug Report (new)\ndescription: Create a bug report\nbody:\n  - type: markdown\n    attributes:\n      label: Description of the problem\n      value: |\n        A clear description of what the bug is, including the expected and actual behavior.\n  - type: markdown\n    attributes:\n      label: Compilation or execution log\n      value: |\n        Any relevant log that may help to understand the bug.\n        Please use [distinct block]https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code) to format this section.\n  - type: markdown\n    attributes:\n      label: Minimal code/script to reproduce the problem\n      value: |\n        Insert your script/code that use QBDI here.\n  - type: markdown\n    attributes:\n      label: Code of the target of the instrumentation\n      value: |\n        Insert your script/code that you try to instrument with QBDI.\n        If the instrumented code is a public library/binary, you may provide a link to download it.\n  - type: markdown\n    attributes:\n      label: Specific command to reproduce your issue\n      value: |\n        Can you list all the command (if possible shell) that should be run to reproduce the bug.\n  - type: markdown\n    attributes:\n      label: Information about the environment\n      value: |\n        Version of your environment and any tools that is needed to reproduce the bug\n        \n        If you use multiples devices (like a computer and a smartphone), please provide the information for each of them.\n\n        - OS: (with version) [e.g. Windows 11, Debian 12.4, Android 14, ...] \n        - Architecture: [e.g. X86, X64, AARCH64, ARM32]\n        - QBDI version (returned by getVersion) and commit hash (if not a release version)\n        - Frida version on the Host and on the target (in case of android device) (for frida/QBDI issue)\n        - Python version (for PyQBDI issue)\n  - type: markdown\n    attributes:\n      label: Additional context or informations\n      value: |\n        Any other context about the problem here.\n\n        Thanks for taking the time to fill out this report!\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: true\n"
  },
  {
    "path": ".github/format_check.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncd $(dirname \"$0\")\nRETURN_CODE=0\nBASEDIR=$(pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\ncd $GITDIR\n\nfor file in $(git ls-files | grep -v -f \"${GITDIR}/.clang-format.exclude-list.txt\" | grep '\\.\\(cpp\\|c\\|h\\|hpp\\)$'); do\n  if [[ -n \"${CLANG_FORMAT_VERSION}\" ]]; then\n    clang-format-\"${CLANG_FORMAT_VERSION}\" -Werror --style=file --verbose -n \"${file}\" || RETURN_CODE=1\n  else\n    clang-format -Werror --style=file --verbose -n \"${file}\" || RETURN_CODE=1\n  fi\ndone\n\ncmake-format -c .cmake_format.conf.py -l info --check $(git ls-files | grep -v -f \"${GITDIR}/.clang-format.exclude-list.txt\" | grep '\\(CMakeLists.txt\\|\\.cmake\\)$')\n\n\nexit ${RETURN_CODE}\n"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "name: Package Android\n\non:\n  push:\n    branches:\n      - master\n      - dev-next\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: ubuntu-22.04\n    strategy:\n      matrix: \n        include:\n          - QBDI_ARCH: 'X86_64'\n            ANDROID_ABI: 'x86_64'\n          - QBDI_ARCH: 'X86'\n            ANDROID_ABI: 'x86'\n          - QBDI_ARCH: 'AARCH64'\n            ANDROID_ABI: 'arm64-v8a'\n          - QBDI_ARCH: 'ARM'\n            ANDROID_ABI: 'armeabi-v7a'\n      fail-fast: false\n    env:\n      QBDI_PLATFORM: 'android'\n      NDK_VERSION: 'r26c'\n      ANDROID_PLATFORM: 23\n    steps:\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-android-${{ matrix.QBDI_ARCH }}-${{ env.NDK_VERSION }}-${{ env.ANDROID_PLATFORM }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-android-${{ matrix.QBDI_ARCH }}-${{ env.NDK_VERSION }}-${{ env.ANDROID_PLATFORM }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache android NDK ${{ env.NDK_VERSION }}\n        uses: actions/cache@v4\n        id: cache-ndk\n        with:\n          path: |\n            android-ndk-${{ env.NDK_VERSION }}/\n          key: ndk-${{ env.NDK_VERSION }}-linux\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - name: Install dependencies\n        run: |\n          sudo apt-get update\n          sudo apt-get install build-essential ccache cmake g++ libstdc++-10-dev make ninja-build pkg-config python3 unzip wget zlib1g-dev\n      - name: Install android NDK ${{ env.NDK_VERSION }}\n        if: steps.cache-ndk.outputs.cache-hit != 'true'\n        run: |\n          wget --no-verbose https://dl.google.com/android/repository/android-ndk-${{ env.NDK_VERSION }}-linux.zip\n          unzip -q android-ndk-${{ env.NDK_VERSION }}-linux.zip\n          rm android-ndk-${{ env.NDK_VERSION }}-linux.zip\n      - run: mkdir -p build\n      - name: Build Package\n        working-directory: ./build\n        run: |\n          cmake -DQBDI_PLATFORM=${{ env.QBDI_PLATFORM }} \\\n                -DQBDI_ARCH=${{ matrix.QBDI_ARCH }} \\\n                -DCMAKE_BUILD_TYPE=Release \\\n                -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/android-ndk-${{ env.NDK_VERSION }}/build/cmake/android.toolchain.cmake \\\n                -DANDROID_ABI=${{ matrix.ANDROID_ABI }} \\\n                -DANDROID_PLATFORM=${{ env.ANDROID_PLATFORM }} \\\n                -DQBDI_TEST=OFF \\\n                -G Ninja \\\n                ..\n          ninja\n          cpack\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_android_${{ matrix.QBDI_ARCH }}\n          path: build/QBDI-*-android-${{ matrix.QBDI_ARCH }}.tar.gz\n\n\n\n"
  },
  {
    "path": ".github/workflows/ccache.conf",
    "content": "max_size = 512 M\n"
  },
  {
    "path": ".github/workflows/clang_format.yml",
    "content": "name: Code format\n\non: [push, pull_request]\n\njobs:\n  check:\n    runs-on: ubuntu-24.04\n    env:\n      CLANG_FORMAT_VERSION: 19\n    steps:\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Set up Python env\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.12\"\n      - name: Install system dependencies\n        run: |\n          sudo apt install -y --no-install-recommends clang-format-${{ env.CLANG_FORMAT_VERSION }}\n          python3 -m pip install cmakelang\n      - name: Check format\n        run: bash ./.github/format_check.sh\n\n\n"
  },
  {
    "path": ".github/workflows/ios.yml",
    "content": "name: Package iOS\n\non:\n  push:\n    branches:\n      - master\n      - dev-next\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: macos-15\n    env:\n      QBDI_ARCH: 'AARCH64'\n      QBDI_PLATFORM: 'ios'\n      MACOSX_DEPLOYMENT_TARGET: '15.0'\n    steps:\n      - name: Set up Python env\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.12\"\n      - name: Install system dependencies\n        run: |\n          brew install cmake ninja ccache\n          python3 -m pip install --upgrade pip setuptools wheel\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-ios-${{ env.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-ios-${{ env.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - run: mkdir -p build\n      - name: build QBDI\n        working-directory: ./build\n        run: |\n          cmake -G Ninja \\\n                -DCMAKE_BUILD_TYPE=Release \\\n                -DQBDI_PLATFORM=${{ env.QBDI_PLATFORM }} \\\n                -DQBDI_ARCH=${{ env.QBDI_ARCH }} \\\n                -DCMAKE_TOOLCHAIN_FILE=\"../cmake/config/ios.toolchain.cmake\" \\\n                ..\n          ninja\n          cpack\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_iOS_${{ env.QBDI_ARCH }}\n          path: build/QBDI-*.tar.gz\n"
  },
  {
    "path": ".github/workflows/linux.yml",
    "content": "name: Tests and Package Linux\n\non:\n  push:\n    branches:\n      - master\n      - dev-next\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix: \n        QBDI_ARCH: ['X86_64', 'X86']\n      fail-fast: false\n    env:\n      QBDI_PLATFORM: 'linux'\n    steps:\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Login to Docker Hub\n        uses: docker/login-action@v3\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-linux-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-linux-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - name: Cache test result\n        uses: actions/cache@v4\n        with:\n          path: |\n            tools/validation_runner/travis_db\n          key: test-linux-${{ matrix.QBDI_ARCH }}\n      - name: Create build image\n        env:\n          QBDI_ARCH: ${{ matrix.QBDI_ARCH }}\n        run: bash ./docker/ci_linux/img_build.sh\n      - name: Build and test QBDI\n        env:\n          QBDI_ARCH: ${{ matrix.QBDI_ARCH }}\n        run: bash ./docker/ci_linux/qbdi.sh\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_linux_${{ matrix.QBDI_ARCH }}\n          path: build/QBDI-*-linux-${{ matrix.QBDI_ARCH }}.tar.gz\n\n  build_ARM_AARCH64:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        QBDI_ARCH: ['AARCH64', 'ARM']\n      fail-fast: false\n    env:\n      QBDI_PLATFORM: 'linux'\n    steps:\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Login to Docker Hub\n        uses: docker/login-action@v3\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-linux-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-linux-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - name: Cache test result\n        uses: actions/cache@v4\n        with:\n          path: |\n            tools/validation_runner/travis_db\n          key: test-linux-${{ matrix.QBDI_ARCH }}\n\n      - name: Create build image\n        run: bash ./docker/ci_linux_arm/images/img_build.sh ${{ matrix.QBDI_ARCH }}\n\n      - name: Build and test QBDI\n        run: bash ./docker/ci_linux_arm/build_qbdi_linux.sh ${{ matrix.QBDI_ARCH }}\n\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_linux_${{ matrix.QBDI_ARCH }}\n          path: build-docker-*/QBDI-*-linux-*.tar.gz\n\n\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: Tests and Package macOS\n\non:\n  push:\n    branches:\n      - master\n      - dev-next\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  build-x86:\n    runs-on: macos-15-intel\n    env:\n      QBDI_ARCH: 'X86_64'\n      QBDI_PLATFORM: 'macos'\n      MACOSX_DEPLOYMENT_TARGET: '10.14'\n      CMAKE_OSX_ARCHITECTURES: \"x86_64\"\n    steps:\n      - name: Set up Python env\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.12\"\n      - name: Install system dependencies\n        run: |\n          brew install cmake ninja ccache\n          python3 -m pip install --upgrade pip setuptools wheel\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-macos-${{ env.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-macos-${{ env.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - run: mkdir -p build\n      - name: build QBDI\n        working-directory: ./build\n        run: |\n          cmake -G Ninja \\\n                -DCMAKE_BUILD_TYPE=Release \\\n                -DQBDI_PLATFORM=${{ env.QBDI_PLATFORM }} \\\n                -DQBDI_ARCH=${{ env.QBDI_ARCH }} \\\n                -DQBDI_TOOLS_VALIDATOR=ON \\\n                -DQBDI_EXAMPLES=ON \\\n                ..\n          ninja\n          cpack\n      - name: test QBDI\n        working-directory: ./build/test\n        run: |\n          ./QBDITest\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_macOS_${{ env.QBDI_ARCH }}\n          path: build/QBDI-*.pkg\n\n  build-aarch64:\n    runs-on: macos-15\n    env:\n      QBDI_ARCH: 'AARCH64'\n      QBDI_PLATFORM: 'macos'\n      MACOSX_DEPLOYMENT_TARGET: '10.14'\n      CMAKE_OSX_ARCHITECTURES: \"arm64\"\n    steps:\n      - name: Set up Python env\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.12\"\n      - name: Install system dependencies\n        run: |\n          brew install cmake ninja ccache\n          python3 -m pip install --upgrade pip setuptools wheel\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-macos-${{ env.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-macos-${{ env.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - run: mkdir -p build\n      - name: build QBDI\n        working-directory: ./build\n        run: |\n          cmake -G Ninja \\\n                -DCMAKE_BUILD_TYPE=Release \\\n                -DQBDI_PLATFORM=${{ env.QBDI_PLATFORM }} \\\n                -DQBDI_ARCH=${{ env.QBDI_ARCH }} \\\n                -DQBDI_TOOLS_VALIDATOR=ON \\\n                -DQBDI_EXAMPLES=ON \\\n                ..\n          ninja\n          cpack\n      - name: test QBDI\n        working-directory: ./build/test\n        run: |\n          ./QBDITest\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_macOS_${{ env.QBDI_ARCH }}\n          path: build/QBDI-*.pkg\n\n  build-aarch64-arm64e:\n    runs-on: macos-15\n    env:\n      QBDI_ARCH: 'AARCH64'\n      QBDI_PLATFORM: 'macos'\n      MACOSX_DEPLOYMENT_TARGET: '10.14'\n      CMAKE_OSX_ARCHITECTURES: \"arm64e\"\n    steps:\n      - name: Set up Python env\n        uses: actions/setup-python@v5\n        with:\n          python-version: \"3.12\"\n      - name: Install system dependencies\n        run: |\n          brew install cmake ninja ccache\n          python3 -m pip install --upgrade pip setuptools wheel\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-macos-${{ env.QBDI_ARCH }}-arm64e-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-macos-${{ env.QBDI_ARCH }}-arm64e-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - run: mkdir -p build\n      - name: build QBDI\n        working-directory: ./build\n        run: |\n          echo 'set(\"CMAKE_OSX_ARCHITECTURES\" \"arm64\" CACHE STRING \"\" FORCE)' > QBDI_llvm_tblgen.cmake\n          cmake -G Ninja \\\n                -DCMAKE_BUILD_TYPE=Release \\\n                -DQBDI_PLATFORM=${{ env.QBDI_PLATFORM }} \\\n                -DQBDI_ARCH=${{ env.QBDI_ARCH }} \\\n                -DQBDI_PTRAUTH=ON \\\n                -DQBDI_TOOLS_VALIDATOR=ON \\\n                -DQBDI_EXAMPLES=ON \\\n                -DQBDI_LLVM_TABLEN_TOOLSCHAIN=$(pwd)/QBDI_llvm_tblgen.cmake \\\n                ..\n          ninja\n          cpack\n      # github action doesn't seems to be able to execute arm64e binary\n      # - name: test QBDI\n      #   working-directory: ./build/test\n      #   run: |\n      #     ./QBDITest\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_macOS_${{ env.QBDI_ARCH }}_arm64e\n          path: build/QBDI-*.pkg\n"
  },
  {
    "path": ".github/workflows/python.yml",
    "content": "name: PyQBDI package\n\non:\n  push:\n    branches:\n      - master\n      - dev-next\n    tags:\n      - 'v*'\n  workflow_dispatch:\n\njobs:\n  build_pyqbdi_linux:\n    uses: ./.github/workflows/python_linux.yml\n    secrets: inherit\n  build_pyqbdi_macos:\n    uses: ./.github/workflows/python_macos.yml\n    secrets: inherit\n  build_pyqbdi_windows:\n    uses: ./.github/workflows/python_windows.yml\n    secrets: inherit\n\n  trigger-rtd:\n    runs-on: ubuntu-latest\n    needs: build_pyqbdi_linux\n    steps:\n      - name: trigger Read The docs build\n        if: github.event_name == 'push'\n        run: |\n          curl -X POST -d \"branches=${BRANCH}\" -d \"token=${RTD_TOKEN}\" https://readthedocs.org/api/v2/webhook/qbdi/145608/\n        env:\n          RTD_TOKEN: ${{ secrets.RTD_TOKEN }}\n          BRANCH: ${{ github.ref_name }}\n\n  all-in-one:\n    name: All PyQBDI whell\n    needs:\n    - build_pyqbdi_linux\n    - build_pyqbdi_macos\n    - build_pyqbdi_windows\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Download all wheel\n      uses: actions/download-artifact@v4\n      with:\n        pattern: PyQBDI_*\n        path: dist/\n        merge-multiple: true\n\n    - name: Export All wheels\n      uses: actions/upload-artifact@v4\n      with:\n        name: All_PyQBDI\n        path: dist/*\n\n  publish-to-testpypi:\n    name: Publish to TestPyPI\n    needs:\n    - build_pyqbdi_linux\n    - build_pyqbdi_macos\n    - build_pyqbdi_windows\n    runs-on: ubuntu-latest\n\n    if: ${{ github.event_name == 'push' && github.ref_name == 'dev-next' && github.ref_type == 'branch' }}\n\n    environment:\n      name: test\n      url: https://test.pypi.org/p/PyQBDI\n\n    permissions:\n      id-token: write\n\n    steps:\n    - name: Download all wheel\n      uses: actions/download-artifact@v4\n      with:\n        pattern: PyQBDI_*\n        path: dist/\n        merge-multiple: true\n\n    - name: Publish to TestPyPI\n      uses: pypa/gh-action-pypi-publish@release/v1\n      with:\n        repository-url: https://test.pypi.org/legacy/\n        skip-existing: true\n\n  publish-to-pypi:\n    name: Publish to PyPI\n    needs:\n    - build_pyqbdi_linux\n    - build_pyqbdi_macos\n    - build_pyqbdi_windows\n    runs-on: ubuntu-latest\n\n    if: startsWith(github.ref, 'refs/tags/v')\n\n    environment:\n      name: release\n      url: https://pypi.org/p/PyQBDI\n\n    permissions:\n      id-token: write\n\n    steps:\n    - name: Download all wheel\n      uses: actions/download-artifact@v4\n      with:\n        pattern: PyQBDI_*\n        path: dist/\n        merge-multiple: true\n\n    - name: Publish to PyPI\n      uses: pypa/gh-action-pypi-publish@release/v1\n"
  },
  {
    "path": ".github/workflows/python_linux.yml",
    "content": "name: PyQBDI Linux package\n\non:\n  workflow_call:\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix: \n        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14']\n        QBDI_ARCH: ['X86_64', 'X86']\n      fail-fast: false\n    env:\n      QBDI_PLATFORM: 'linux'\n    steps:\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-pyqbdi-linux-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-pyqbdi-linux-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - name: Create build image\n        env:\n          QBDI_ARCH: ${{ matrix.QBDI_ARCH }}\n        run: bash ./docker/ci_python_linux/img_build.sh\n      - name: Build Wheel for python ${{ matrix.python-version }}\n        env:\n          QBDI_ARCH: ${{ matrix.QBDI_ARCH }}\n        run: bash ./docker/ci_python_linux/whl_build.sh ${{ matrix.python-version }}\n      - name: Export wheel ${{ matrix.python-version }}\n        uses: actions/upload-artifact@v4\n        with:\n          name: PyQBDI_linux_${{ matrix.QBDI_ARCH }}_python_${{ matrix.python-version }}\n          path: outwheel/*\n\n"
  },
  {
    "path": ".github/workflows/python_macos.yml",
    "content": "name: PyQBDI macOS package\n\non:\n  workflow_call:\n  workflow_dispatch:\n\njobs:\n  build-pyqbdi-x64:\n    runs-on: macos-15-intel\n    strategy:\n      matrix:\n        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14']\n      fail-fast: false\n    env:\n      QBDI_PLATFORM: 'macos'\n      QBDI_ARCH: 'X86_64'\n      MACOSX_DEPLOYMENT_TARGET: '10.14'\n      _PYTHON_HOST_PLATFORM: \"macosx-10.14-x86_64\"\n      CMAKE_OSX_ARCHITECTURES: \"x86_64\"\n    steps:\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ matrix.python-version }}\n          allow-prereleases: ${{ matrix.python-version == '3.15' }}\n      - name: Install system dependencies\n        run: |\n          brew install cmake ninja ccache\n          python3 -m pip install --upgrade pip setuptools wheel build\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache-python\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-macos-X86_64-${{ hashFiles('.github/workflows/ccache.conf') }}-python-${{ matrix.python-version }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-macos-X86_64-${{ hashFiles('.github/workflows/ccache.conf') }}-python-${{ matrix.python-version }}-\n            ccache-macos-X86_64-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache-python.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - name: Build PyQBDI ${{ matrix.python-version }}\n        run: |\n          python3 --version\n          python3 -m build -w\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: PyQBDI_macOS_X86_64_${{ matrix.python-version }}\n          path: dist/*\n\n  build-pyqbdi-aarch64:\n    runs-on: macos-15\n    strategy:\n      matrix:\n        python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']\n      fail-fast: false\n    env:\n      QBDI_ARCH: 'AARCH64'\n      QBDI_PLATFORM: 'macos'\n      MACOSX_DEPLOYMENT_TARGET: '11.0'\n      _PYTHON_HOST_PLATFORM: \"macosx-11.0-arm64\"\n      CMAKE_OSX_ARCHITECTURES: \"arm64\"\n    steps:\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ matrix.python-version }}\n          allow-prereleases: ${{ matrix.python-version == '3.15' }}\n      - name: Install system dependencies\n        run: |\n          brew install cmake ninja ccache\n          python3 -m pip install --upgrade pip setuptools wheel build\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache ccache\n        uses: actions/cache@v4\n        id: cache-ccache-python\n        with:\n          path: |\n            ~/.ccache\n          key: ccache-macos-AARCH64-${{ hashFiles('.github/workflows/ccache.conf') }}-python-${{ matrix.python-version }}-${{ github.run_number }}\n          restore-keys: |\n            ccache-macos-AARCH64-${{ hashFiles('.github/workflows/ccache.conf') }}-python-${{ matrix.python-version }}-\n            ccache-macos-AARCH64-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - if: steps.cache-ccache-python.outputs.cache-hit != 'true'\n        run: |\n          mkdir -p ~/.ccache\n          cp .github/workflows/ccache.conf ~/.ccache/ccache.conf\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - name: Build PyQBDI ${{ matrix.python-version }}\n        run: |\n          python3 --version\n          python3 -m build -w\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: PyQBDI_macOS_AARCH64_${{ matrix.python-version }}\n          path: dist/*\n\n#  build-pyqbdi-aarch64-arm64e:\n#    runs-on: macos-15\n#    env:\n#      QBDI_ARCH: 'AARCH64'\n#      QBDI_PLATFORM: 'macos'\n#      MACOSX_DEPLOYMENT_TARGET: '11.0'\n#      _PYTHON_HOST_PLATFORM: 'macosx-11.0-arm64'\n#      CMAKE_OSX_ARCHITECTURES: 'arm64e'\n#      OPENSSL_VERSION: '3.5.4'\n#      OPENSSL_PREFIX: ${{ github.workspace }}/openssl-build\n#      PYTHON_VERSION: '3.14.0'\n#      PYTHON_PREFIX: ${{ github.workspace }}/python-build\n#      PYTHON_VENV: ${{ github.workspace }}/python-venv\n#    steps:\n#      - name: Download OpenSSL\n#        run: |\n#          wget -O- https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz | tar xJf -\n#      - name: Build OpenSSL for arm64e\n#        working-directory: openssl-${{ env.OPENSSL_VERSION }}\n#        run: |\n#          sed -i'' -e 's/-arch arm64/-arch arm64e/g' Configurations/10-main.conf\n#          ./configure --prefix=${OPENSSL_PREFIX} --openssldir=${OPENSSL_PREFIX}/ssl\n#          make -j$(nproc)\n#          make install\n#      - name: Download Python\n#        run: |\n#          wget -O- https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz | tar xJf -\n#      - name: Build Python for arm64e\n#        working-directory: Python-${{ env.PYTHON_VERSION }}\n#        run: |\n#          ./configure --prefix=${PYTHON_PREFIX} --with-pydebug --with-openssl=${OPENSSL_PREFIX} \\\n#            CFLAGS='-arch arm64e' LDFLAGS='-arch arm64e'\n#          make -j$(nproc)\n#          make install\n#      - name: Checkout QBDI\n#        uses: actions/checkout@v4\n#      - name: Install system dependencies\n#        run: |\n#          brew install cmake\n#      - name: Create Python virtual environment\n#        run: |\n#          ${PYTHON_PREFIX}/bin/python3 -m venv ${PYTHON_VENV}\n#          ${PYTHON_VENV}/bin/python3 -m pip install --upgrade pip setuptools wheel build\n#      - name: Build PyQBDI ${PYTHON_VERSION}\n#        run: |\n#          ${PYTHON_VENV}/bin/python3 -m build -w\n#      - name: Export package\n#        uses: actions/upload-artifact@v4\n#        with:\n#          name: PyQBDI_macOS_AARCH64_arm64e_${PYTHON_VERSION}\n#          path: dist/*\n"
  },
  {
    "path": ".github/workflows/python_windows.yml",
    "content": "name: PyQBDI Windows Package\n\non:\n  workflow_call:\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: windows-latest\n    strategy:\n      matrix:\n        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14']\n        QBDI_ARCH: ['X86_64', 'X86']\n        include:\n          - QBDI_ARCH: 'X86_64'\n            WINDOWS_ARCH: 'x64'\n          - QBDI_ARCH: 'X86'\n            WINDOWS_ARCH: 'x86'\n      fail-fast: false\n    env:\n      QBDI_PLATFORM: 'windows'\n      SCCACHE_CACHE_SIZE: \"1G\"\n      SCCACHE_DIR: \"C:\\\\Users\\\\runner\\\\AppData\\\\Local\\\\Mozilla\\\\sccache\"\n    steps:\n      - name: Install system dependencies\n        run: |\n          choco install sccache\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache sccache\n        uses: actions/cache@v4\n        id: cache-sccache\n        with:\n          path: |\n            ${{ env.SCCACHE_DIR }}\n          key: sccache-win-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ matrix.python-version }}-${{ github.run_number }}\n          restore-keys: |\n            sccache-win-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ matrix.python-version }}-\n            sccache-win-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - name: Create sccache directory\n        if: steps.cache-sccache.outputs.cache-hit != 'true'\n        shell: powershell\n        run: |\n          If(!(test-path -PathType container ${env:SCCACHE_DIR}))\n          {\n            New-Item -ItemType Directory -Path ${env:SCCACHE_DIR}\n          }\n      - uses: ilammy/msvc-dev-cmd@v1\n        with:\n          arch: ${{ matrix.WINDOWS_ARCH }}\n      - name: Start sccache server\n        run: sccache --start-server\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v6\n        with:\n          python-version: ${{ matrix.python-version }}\n          allow-prereleases: ${{ matrix.python-version == '3.15' }}\n          architecture: ${{ matrix.WINDOWS_ARCH }}\n      - name: build PyQBDI ${{ matrix.python-version }}\n        run: |\n          python --version\n          python -m pip --version\n          python -m pip install --upgrade pip\n          python -m pip install setuptools wheel build\n          python -m build -w\n\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: PyQBDI_windows_${{ matrix.QBDI_ARCH }}_python_${{ matrix.python-version }}\n          path: dist\\PyQBDI-*.whl\n      - name: Stop sccache server\n        run: sccache --stop-server\n\n"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "name: Tests and Package Windows\n\non:\n  push:\n    branches:\n      - master\n      - dev-next\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: windows-latest\n    strategy:\n      matrix:\n        include:\n          - QBDI_ARCH: 'X86_64'\n            WINDOWS_ARCH: 'x64'\n          - QBDI_ARCH: 'X86'\n            WINDOWS_ARCH: 'x86'\n      fail-fast: false\n    env:\n      QBDI_PLATFORM: 'windows'\n      SCCACHE_CACHE_SIZE: \"1G\"\n      SCCACHE_DIR: \"C:\\\\Users\\\\runner\\\\AppData\\\\Local\\\\Mozilla\\\\sccache\"\n    steps:\n      - name: Install system dependencies\n        run: |\n          choco install sccache nsis\n      - name: checkout\n        uses: actions/checkout@v4\n      - name: Cache sccache\n        uses: actions/cache@v4\n        id: cache-sccache\n        with:\n          path: |\n            ${{ env.SCCACHE_DIR }}\n          key: sccache-win-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-${{ github.run_number }}\n          restore-keys: |\n            sccache-win-${{ matrix.QBDI_ARCH }}-${{ hashFiles('.github/workflows/ccache.conf') }}-\n      - name: Create sccache directory\n        if: steps.cache-sccache.outputs.cache-hit != 'true'\n        shell: powershell\n        run: |\n          If(!(test-path -PathType container ${env:SCCACHE_DIR}))\n          {\n            New-Item -ItemType Directory -Path ${env:SCCACHE_DIR}\n          }\n      - uses: ilammy/msvc-dev-cmd@v1\n        with:\n          arch: ${{ matrix.WINDOWS_ARCH }}\n      - name: Start sccache server\n        run: sccache --start-server\n      - name: Cache third-party\n        uses: actions/cache@v4\n        with:\n          path: |\n            third-party\n          key: QBDI-third-party-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('**/*.cmake') }}\n      - run: mkdir build\n      - name: Configure\n        working-directory: .\\build\n        run: cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CROSSCOMPILING=FALSE -DQBDI_PLATFORM=${{ env.QBDI_PLATFORM }} -DQBDI_ARCH=${{ matrix.QBDI_ARCH }} -DQBDI_TOOLS_VALIDATOR=OFF -DQBDI_TOOLS_PYQBDI=OFF -DQBDI_EXAMPLES=ON ..\n      - name: build QBDI\n        working-directory: .\\build\n        run: ninja\n      - name: package QBDI\n        working-directory: .\\build\n        run: cpack\n      - name: test QBDI\n        working-directory: .\\build\\test\n        run: |\n          .\\QBDITest\n      - name: Export package\n        uses: actions/upload-artifact@v4\n        with:\n          name: package_Windows_${{ matrix.WINDOWS_ARCH }}\n          path: build/QBDI-*.exe\n      - name: Stop sccache server\n        run: sccache --stop-server\n\n"
  },
  {
    "path": ".gitignore",
    "content": "**/.DS_Store\nTODO\nbuild*/*\n\ndist\noutwheel\n\ndocs/build\ndocs/doxygen_c\ndocs/doxygen_cpp\ndocs/doxygen_qbdipreload\ndocs/qbdi_c.doxygen\ndocs/qbdi_cpp.doxygen\ndocs/qbdipreload.doxygen\n\nqbdi-deps.tar.gz\nqbdi.tar.gz\npackage/QBDI-*\npackage/package-*\n\nthird-party\n\ntools/validation_runner/.*_coverage\ntools/validation_runner/.*_output\ntools/validation_runner/.*_result\ntools/validation_runner/*.db\ntools/validation_runner/travis_db\n\n__pycache__\n*.pyc\n*.egg-info\n"
  },
  {
    "path": ".readthedocs.condaenv.yml",
    "content": "name: RTD\nchannels:\n  - conda-forge\n  - defaults\ndependencies:\n  - python=3.11\n  - doxygen=1.9.8\n  - pip\n  - pip:\n    - python-dateutil\n    - pygit2\n    - requests\n    - Sphinx==5.3.0\n    - docutils==0.18.1\n    - breathe==4.35.0\n    - sphinx-js==3.2.1\n    - markupsafe==2.0.1\n    - sphinx_rtd_theme==1.3.0\n"
  },
  {
    "path": ".readthedocs.yml",
    "content": "version: 2\n\nbuild:\n  os: ubuntu-24.04\n  tools:\n    python: \"miniconda3-4.7\"\n    nodejs: \"20\"\n  jobs:\n    post_create_environment:\n      - npm install -g jsdoc\n\n# install doxygen >= 1.9.2\nconda:\n  environment: .readthedocs.condaenv.yml\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n  configuration: docs/source/conf.py\n\nformats:\n  - htmlzip\n  - pdf\n\npython:\n  install:\n    - method: setuptools\n      path: docs/rtd_pyqbdi_artifact\n"
  },
  {
    "path": "CHANGELOG",
    "content": "Changelog\n=========\n\nsee docs/source/changelog.rst or https://qbdi.readthedocs.io/en/stable/changelog.html\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28)\nproject(QBDI)\n\ninclude(GNUInstallDirs)\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/\")\n\n# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:\ncmake_policy(SET CMP0135 NEW)\n\n# Config platform & arch\ninclude(QBDIConfig)\n# Option\ninclude(QBDIOptions)\n\n# Force find system utils (strip)\ninclude(CMakeFindBinUtils)\n\n# Macros\ninclude(merge_archives)\n\n# CCACHE\n# ======\nif(QBDI_CCACHE)\n  if(CMAKE_SYSTEM_NAME STREQUAL \"Windows\")\n    find_program(SCCACHE_FOUND sccache)\n    if(SCCACHE_FOUND)\n      set(CMAKE_C_COMPILER_LAUNCHER ${SCCACHE_FOUND})\n      set(CMAKE_CXX_COMPILER_LAUNCHER ${SCCACHE_FOUND})\n      message(STATUS \"found SCCACHE at ${SCCACHE_FOUND}\")\n    else()\n      set(QBDI_CCACHE OFF)\n      message(WARNING \"CCACHE or SCCACHE not found: disabled\")\n    endif()\n  else()\n    find_program(CCACHE_FOUND ccache)\n    if(CCACHE_FOUND)\n      set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_FOUND})\n      set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_FOUND})\n      message(STATUS \"found CCACHE at ${CCACHE_FOUND}\")\n    else()\n      set(QBDI_CCACHE OFF)\n      message(WARNING \"CCACHE or SCCACHE not found: disabled\")\n    endif()\n  endif()\nendif()\n\ninclude(CheckCCompilerFlag)\n\nif(QBDI_ASAN)\n  set(CMAKE_REQUIRED_FLAGS \"-Werror -fsanitize=address\")\n  check_c_compiler_flag(\"-fsanitize=address\" HAVE_FLAG_SANITIZE_ADDRESS)\n  unset(CMAKE_REQUIRED_FLAGS)\n  if(HAVE_FLAG_SANITIZE_ADDRESS)\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fsanitize=address\")\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fsanitize=address\")\n    message(STATUS \"ASAN is activated\")\n  else()\n    message(WARNING \"ASAN can't be activated\")\n  endif()\nendif()\n\n# Configure RPATH on OS X\n\nif(APPLE)\n  set(CMAKE_MACOSX_RPATH TRUE)\n\n  # use, i.e. don't skip the full RPATH for the build tree\n  set(CMAKE_SKIP_BUILD_RPATH FALSE)\n\n  # when building, don't use the install RPATH already\n  # (but later on when installing)\n  set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)\n\n  set(CMAKE_INSTALL_RPATH \"${CMAKE_INSTALL_PREFIX}/lib\")\n\n  # add the automatically determined parts of the RPATH\n  # which point to directories outside the build tree to the install RPATH\n  set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)\n\n  # the RPATH to be used when installing, but only if it's not a system directory\n  list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES\n       \"${CMAKE_INSTALL_PREFIX}/lib\" isSystemDir)\n  if(\"${isSystemDir}\" STREQUAL \"-1\")\n    set(CMAKE_INSTALL_RPATH \"${CMAKE_INSTALL_PREFIX}/lib\")\n  endif(\"${isSystemDir}\" STREQUAL \"-1\")\nendif()\n\nset(QBDI_EXPORT_SYM 1)\nconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/include/QBDI/Config.h.in\"\n               \"${CMAKE_CURRENT_BINARY_DIR}/include-shared/QBDI/Config.h\" @ONLY)\nunset(QBDI_EXPORT_SYM)\nconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/include/QBDI/Config.h.in\"\n               \"${CMAKE_CURRENT_BINARY_DIR}/include-static/QBDI/Config.h\" @ONLY)\nconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/include/QBDI/Version.h.in\"\n               \"${CMAKE_CURRENT_BINARY_DIR}/include/QBDI/Version.h\" @ONLY)\nconfigure_file(\n  \"${CMAKE_CURRENT_SOURCE_DIR}/include/QBDI/arch/${QBDI_ARCH}/State.h\"\n  \"${CMAKE_CURRENT_BINARY_DIR}/include/QBDI/State.h\" COPYONLY)\nconfigure_file(\n  \"${CMAKE_CURRENT_SOURCE_DIR}/include/QBDI/arch/${QBDI_ARCH}/Options.h\"\n  \"${CMAKE_CURRENT_BINARY_DIR}/include/QBDI/Options.h\" COPYONLY)\nconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/src/Utility/Version_commit.h.in\"\n               \"${CMAKE_CURRENT_BINARY_DIR}/src/Utility/Version_commit.h\" @ONLY)\n\n# log debug\nset(QBDI_COMMON_DEFINITION)\nif(QBDI_LOG_DEBUG)\n  message(STATUS \"Compiling with QBDI_LOG_DEBUG\")\n  set(QBDI_COMMON_DEFINITION ${QBDI_COMMON_DEFINITION} -D_QBDI_LOG_DEBUG)\nendif()\n\nif(QBDI_PLATFORM_WINDOWS)\n  set(QBDI_COMMON_C_FLAGS\n      /DWIN32\n      /D_WINDOWS\n      /W3\n      /MP\n      /permissive-\n      -D_CRT_SECURE_NO_DEPRECATE\n      -D_CRT_SECURE_NO_WARNINGS\n      -D_CRT_NONSTDC_NO_DEPRECATE\n      -D_CRT_NONSTDC_NO_WARNINGS\n      -D_SCL_SECURE_NO_DEPRECATE\n      -D_SCL_SECURE_NO_WARNINGS\n      -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR\n      -w14062\n      -wd4141\n      -wd4146\n      -wd4180\n      -wd4244\n      -wd4258\n      -wd4267\n      -wd4291\n      -wd4345\n      -wd4351\n      -wd4355\n      -wd4456\n      -wd4457\n      -wd4458\n      -wd4459\n      -wd4503\n      -wd4624\n      -wd4722\n      -wd4800\n      -we4238\n      -D__STDC_CONSTANT_MACROS\n      -D__STDC_FORMAT_MACROS\n      -D__STDC_LIMIT_MACROS)\n  set(QBDI_COMMON_CXX_FLAGS\n      /DWIN32\n      /D_WINDOWS\n      /W3\n      /MP\n      /permissive-\n      -D_CRT_SECURE_NO_DEPRECATE\n      -D_CRT_SECURE_NO_WARNINGS\n      -D_CRT_NONSTDC_NO_DEPRECATE\n      -D_CRT_NONSTDC_NO_WARNINGS\n      -D_SCL_SECURE_NO_DEPRECATE\n      -D_SCL_SECURE_NO_WARNINGS\n      -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR\n      -w14062\n      -wd4141\n      -wd4146\n      -wd4180\n      -wd4244\n      -wd4258\n      -wd4267\n      -wd4291\n      -wd4345\n      -wd4351\n      -wd4355\n      -wd4456\n      -wd4457\n      -wd4458\n      -wd4459\n      -wd4503\n      -wd4624\n      -wd4722\n      -wd4800\n      -we4238\n      -D__STDC_CONSTANT_MACROS\n      -D__STDC_FORMAT_MACROS\n      -D__STDC_LIMIT_MACROS)\n  # set _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING for llvm compile\n  set(CMAKE_CXX_FLAGS\n      \"${CMAKE_CXX_FLAGS} -D_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING\"\n  )\nelse()\n  set(QBDI_COMMON_C_FLAGS -Wall -ffunction-sections -fdata-sections\n                          -fvisibility-inlines-hidden -fvisibility=hidden -fPIC)\n  set(QBDI_COMMON_CXX_FLAGS\n      -Wall\n      -ffunction-sections\n      -fdata-sections\n      -fvisibility-inlines-hidden\n      -fvisibility=hidden\n      -fPIC\n      -fno-rtti)\n  if(CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n    list(APPEND QBDI_COMMON_C_FLAGS -Wno-missing-template-keyword)\n    list(APPEND QBDI_COMMON_CXX_FLAGS -Wno-missing-template-keyword)\n    check_c_compiler_flag(-Wno-psabi HAS_NO_PSABI)\n    if(HAS_NO_PSABI)\n      list(APPEND QBDI_COMMON_C_FLAGS -Wno-psabi)\n      list(APPEND QBDI_COMMON_CXX_FLAGS -Wno-psabi)\n    endif()\n  endif()\n  if(NOT QBDI_PLATFORM_ANDROID)\n    set(QBDI_COMMON_DEFINITION\n        ${QBDI_COMMON_DEFINITION} -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS\n        -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS)\n  endif()\nendif()\n\n# AVX\nif(QBDI_DISABLE_AVX)\n  set(QBDI_COMMON_DEFINITION ${QBDI_COMMON_DEFINITION}\n                             -D_QBDI_FORCE_DISABLE_AVX)\nendif()\n\n# Force 32bit\nif(QBDI_PLATFORM_MACOS AND QBDI_ARCH_X86)\n  set(CMAKE_C_FLAGS \"-arch i386 ${CMAKE_C_FLAGS}\")\n  set(CMAKE_CXX_FLAGS \"-arch i386 ${CMAKE_CXX_FLAGS}\")\nelseif((QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID) AND QBDI_ARCH_X86)\n  set(CMAKE_C_FLAGS \"-m32 ${CMAKE_C_FLAGS}\")\n  set(CMAKE_CXX_FLAGS \"-m32 ${CMAKE_CXX_FLAGS}\")\nendif()\n\n# Disable thumb on ARM (until we support it)\nif(QBDI_ARCH_ARM)\n  # detect if -mthumb-interwork is needed\n  check_c_compiler_flag(-mthumb-interwork HAS_THUMB_INTERWORK)\n  if(HAS_THUMB_INTERWORK)\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -mthumb-interwork\")\n    set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -mthumb-interwork\")\n    set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -mthumb-interwork\")\n    set(CMAKE_SHARED_LINKER_FLAGS\n        \"${CMAKE_SHARED_LINKER_FLAGS} -mthumb-interwork\")\n    set(CMAKE_MODULE_LINKER_FLAGS\n        \"${CMAKE_MODULE_LINKER_FLAGS} -mthumb-interwork\")\n  endif()\nendif()\n\n# Dependencies\n# ============\ninclude(QBDIDependencies)\n\n# Setup assembly compile\nif(QBDI_PLATFORM_WINDOWS)\n  enable_language(ASM_MASM)\n  set(ASM_EXT \"asm\")\nelseif(QBDI_ARCH_X86 OR QBDI_ARCH_X86_64)\n  if(NOT QBDI_PLATFORM_ANDROID)\n    set(CMAKE_ASM-ATT_COMPILER ${AS_BINARY})\n    enable_language(ASM-ATT)\n  else()\n    enable_language(ASM)\n  endif()\n  set(ASM_EXT \"s\")\n  if(QBDI_PLATFORM_MACOS AND QBDI_ARCH_X86)\n    set(CMAKE_ASM-ATT_FLAGS \"-arch i386\")\n  elseif((QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID) AND QBDI_ARCH_X86)\n    set(CMAKE_ASM-ATT_FLAGS \"--32\")\n  endif()\nelse()\n  enable_language(ASM)\n  if(QBDI_PLATFORM_IOS)\n    if(QBDI_ARCH_AARCH64)\n      set(CMAKE_ASM_FLAGS \"-arch arm64\")\n    else()\n      set(CMAKE_ASM_FLAGS \"-arch armv7\")\n    endif()\n  endif()\nendif()\n\n# Add QBDI source file in QBDI_obj\nadd_library(QBDI_src INTERFACE)\nadd_library(QBDI_shared_src INTERFACE)\n\ntarget_link_libraries(QBDI_src INTERFACE qbdi-llvm spdlog)\n\ninclude(\"${CMAKE_CURRENT_SOURCE_DIR}/src/CMakeLists.txt\")\n\ntarget_compile_features(QBDI_src INTERFACE cxx_std_17)\ntarget_compile_options(\n  QBDI_src INTERFACE $<$<COMPILE_LANGUAGE:C>:${QBDI_COMMON_C_FLAGS}>)\ntarget_compile_options(\n  QBDI_src INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${QBDI_COMMON_CXX_FLAGS}>)\ntarget_compile_definitions(QBDI_src INTERFACE ${QBDI_COMMON_DEFINITION})\ntarget_include_directories(\n  QBDI_src\n  INTERFACE \"${CMAKE_CURRENT_BINARY_DIR}/include\"\n            \"${CMAKE_CURRENT_SOURCE_DIR}/include\"\n            \"${CMAKE_CURRENT_SOURCE_DIR}/src\" \"${CMAKE_CURRENT_BINARY_DIR}/src\")\n\ntarget_include_directories(\n  QBDI_shared_src INTERFACE \"${CMAKE_CURRENT_BINARY_DIR}/include\"\n                            \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\n\nif(QBDI_STATIC_LIBRARY)\n  add_library(QBDI_obj STATIC)\n  target_link_libraries(QBDI_obj PRIVATE QBDI_src)\n  set_target_properties(QBDI_obj PROPERTIES POSITION_INDEPENDENT_CODE ON)\n  target_include_directories(\n    QBDI_obj PUBLIC \"${CMAKE_CURRENT_BINARY_DIR}/include-static\")\n\n  set(qbdi_static_libs qbdi-llvm QBDI_obj spdlog)\n\n  merge_static_libs(QBDI_static QBDI \\${qbdi_static_libs})\n\n  add_library(QBDI::QBDI_static ALIAS QBDI_static)\n  target_include_directories(\n    QBDI_static\n    PUBLIC $<INSTALL_INTERFACE:include>\n           $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n           $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>\n           $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include-static>)\n\n  target_link_libraries(QBDI_static PUBLIC ${QBDI_LLVM_LINK_LIBRARY})\n  if(QBDI_PLATFORM_ANDROID)\n    target_link_libraries(QBDI_static PUBLIC log)\n  endif()\n  add_dependencies(QBDI_static QBDI_obj)\n\n  if(QBDI_PLATFORM_WINDOWS)\n    set_target_properties(QBDI_static PROPERTIES OUTPUT_NAME QBDI_static)\n  endif()\n\n  install(\n    TARGETS QBDI_static\n    EXPORT QBDI_targets\n    COMPONENT QBDI\n    LIBRARY DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n    ARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n    RUNTIME DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\n\nendif()\n\n# QBDI as a shared library\nif(QBDI_SHARED_LIBRARY)\n  add_library(QBDI SHARED)\n  add_library(QBDI::QBDI ALIAS QBDI)\n\n  target_include_directories(\n    QBDI\n    PUBLIC $<INSTALL_INTERFACE:include>\n           $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n           $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>\n    INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include-static>\n    PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include-shared>)\n\n  target_link_libraries(QBDI PRIVATE QBDI_src QBDI_shared_src qbdi-llvm)\n\n  set_target_properties(QBDI PROPERTIES OUTPUT_NAME QBDI)\n\n  if(DEFINED STRIP_PATH)\n    # Force a strip cmd\n    set(CMAKE_STRIP ${STRIP_PATH})\n  endif()\n  if(DEFINED CMAKE_STRIP\n     AND NOT CMAKE_STRIP STREQUAL \"CMAKE_STRIP-NOTFOUND\"\n     AND NOT CMAKE_BUILD_TYPE STREQUAL \"Debug\"\n     AND NOT QBDI_ENABLE_LOG_DEBUG)\n\n    set(STRIP_ARGS \"\")\n    if(APPLE)\n      set(STRIP_ARGS ${STRIP_ARGS} -un)\n    elseif(UNIX)\n      set(STRIP_ARGS ${STRIP_ARGS} -x)\n    endif()\n    add_custom_command(\n      TARGET QBDI\n      POST_BUILD\n      COMMAND ${CMAKE_STRIP} ${STRIP_ARGS} \"$<TARGET_FILE:QBDI>\")\n  endif()\n\n  # Install QBDI targets\n  install(\n    TARGETS QBDI\n    EXPORT QBDI_targets\n    COMPONENT QBDI\n    LIBRARY DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n    ARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\"\n    RUNTIME DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\n\n  if(QBDI_PLATFORM_WINDOWS)\n    install(\n      FILES ${CMAKE_CURRENT_BINARY_DIR}/QBDI.exp\n      COMPONENT QBDI\n      DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\n  endif()\n\nendif()\n\nset(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME \"QBDI\")\ninstall(\n  DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/include/QBDI\"\n  DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\"\n  PATTERN \"*.in\" EXCLUDE\n  PATTERN \"arch\" EXCLUDE)\ninstall(DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/include/QBDI\"\n        DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\")\ninstall(DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/include-static/QBDI\"\n        DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\")\n\ninstall(FILES \"${CMAKE_CURRENT_SOURCE_DIR}/include/QBDI.h\"\n        DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\")\n\nif(QBDI_INSTALL)\n  # Configure install\n  if(UNIX)\n    set(FULL_PACKAGE true)\n  else()\n    set(FULL_PACKAGE false)\n  endif()\n\n  if(FULL_PACKAGE)\n    #string(REPLACE \";\" \" \" LLVM_LIBS_STR \"${LLVM_LIBS}\")\n    #configure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/package/qbdi.pc.in\"\n    #               \"${CMAKE_CURRENT_BINARY_DIR}/package/qbdi.pc\" @ONLY)\n    #install(FILES \"${CMAKE_CURRENT_BINARY_DIR}/package/qbdi.pc\"\n    #        DESTINATION \"${CMAKE_INSTALL_LIBDIR}/pkgconfig/\")\n\n    set(RESOURCES_PREFIX \"${CMAKE_INSTALL_DATADIR}/qbdi${QBDI_ARCH}\")\n\n    install(\n      FILES \"${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt\"\n      DESTINATION \"${RESOURCES_PREFIX}\"\n      COMPONENT QBDI)\n\n    install(\n      EXPORT QBDI_targets\n      FILE QBDIConfig.cmake\n      NAMESPACE QBDI::\n      DESTINATION ${RESOURCES_PREFIX}/cmake)\n\n    install(\n      EXPORT QBDI_targets\n      FILE QBDI${QBDI_ARCH}Config.cmake\n      NAMESPACE QBDI::${QBDI_ARCH}::\n      DESTINATION ${RESOURCES_PREFIX}/cmake)\n\n    # QBDI template\n    configure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/package/qbdi-template.in\"\n                   \"${CMAKE_CURRENT_BINARY_DIR}/package/qbdi-template\")\n    configure_file(\n      \"${CMAKE_CURRENT_SOURCE_DIR}/templates/qbdi_template/CMakeLists.txt.in\"\n      \"${CMAKE_CURRENT_BINARY_DIR}/templates/qbdi_template/CMakeLists.txt\"\n      @ONLY)\n    install(\n      DIRECTORY templates/qbdi_template\n      DESTINATION \"${RESOURCES_PREFIX}\"\n      COMPONENT QBDITemplate\n      PATTERN \"CMakeLists.txt.in\" EXCLUDE)\n    install(\n      FILES \"${CMAKE_CURRENT_BINARY_DIR}/templates/qbdi_template/CMakeLists.txt\"\n      DESTINATION \"${RESOURCES_PREFIX}/qbdi_template\")\n    install(\n      PROGRAMS \"${CMAKE_CURRENT_BINARY_DIR}/package/qbdi-template\"\n      DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n      COMPONENT QBDITemplate\n      RENAME \"qbdi-template-${QBDI_ARCH}\")\n\n    set(PRELOAD_RESOURCES_PREFIX\n        \"${CMAKE_INSTALL_DATADIR}/qbdipreload${QBDI_ARCH}\")\n    # QBDI preload template\n    if(QBDI_TOOLS_QBDIPRELOAD)\n      configure_file(\n        \"${CMAKE_CURRENT_SOURCE_DIR}/package/qbdi-preload-template.in\"\n        \"${CMAKE_CURRENT_BINARY_DIR}/package/qbdi-preload-template\")\n      configure_file(\n        \"${CMAKE_CURRENT_SOURCE_DIR}/templates/qbdi_preload_template/CMakeLists.txt.in\"\n        \"${CMAKE_CURRENT_BINARY_DIR}/templates/qbdi_preload_template/CMakeLists.txt\"\n        @ONLY)\n      install(\n        DIRECTORY templates/qbdi_preload_template\n        DESTINATION \"${PRELOAD_RESOURCES_PREFIX}\"\n        COMPONENT QBDITemplate\n        PATTERN \"CMakeLists.txt.in\" EXCLUDE)\n      install(\n        FILES\n          \"${CMAKE_CURRENT_BINARY_DIR}/templates/qbdi_preload_template/CMakeLists.txt\"\n        DESTINATION \"${PRELOAD_RESOURCES_PREFIX}/qbdi_preload_template\")\n      install(\n        PROGRAMS \"${CMAKE_CURRENT_BINARY_DIR}/package/qbdi-preload-template\"\n        DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n        COMPONENT QBDITemplate\n        RENAME \"qbdi-preload-template-${QBDI_ARCH}\")\n    endif()\n\n    # Frida template\n    if(QBDI_TOOLS_FRIDAQBDI)\n      configure_file(package/qbdi-frida-template.in package/qbdi-frida-template)\n      install(\n        DIRECTORY templates/qbdi_frida_template\n        DESTINATION \"${RESOURCES_PREFIX}\"\n        COMPONENT QBDIFridaTemplate)\n      install(\n        PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/package/qbdi-frida-template\n        DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n        COMPONENT QBDIFridaTemplate\n        RENAME \"qbdi-frida-template-${QBDI_ARCH}\")\n      install(FILES tools/frida-qbdi.js DESTINATION \"${RESOURCES_PREFIX}\")\n    endif()\n\n  else()\n    install(\n      EXPORT QBDI_targets\n      FILE QBDIConfig.cmake\n      NAMESPACE QBDI::\n      DESTINATION cmake)\n\n    install(\n      EXPORT QBDI_targets\n      FILE QBDI${QBDI_ARCH}Config.cmake\n      NAMESPACE QBDI::${QBDI_ARCH}::\n      DESTINATION cmake)\n\n    install(FILES \"${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt\" DESTINATION .)\n\n    install(DIRECTORY templates/qbdi_template/\n            DESTINATION templates/qbdi_template)\n\n    if(QBDI_TOOLS_QBDIPRELOAD)\n      install(DIRECTORY templates/qbdi_preload_template\n              DESTINATION templates/qbdi_preload_template)\n    endif()\n\n    if(QBDI_TOOLS_FRIDAQBDI)\n      install(DIRECTORY templates/qbdi_frida_template\n              DESTINATION templates/qbdi_frida_template)\n      install(FILES tools/frida-qbdi.js DESTINATION .)\n    endif()\n\n    install(FILES LICENSE.txt package/Readme.txt DESTINATION .)\n\n    set(PRELOAD_RESOURCES_PREFIX qbdipreload)\n  endif()\nendif()\n\n# Configure iOS code signature\nif(QBDI_PLATFORM_IOS)\n  # Find codesign executable\n  execute_process(\n    COMMAND xcrun -f codesign\n    OUTPUT_STRIP_TRAILING_WHITESPACE\n    OUTPUT_VARIABLE CODESIGN)\n  # Define a code signature macro\n  macro(ADD_SIGNATURE target_name)\n    add_custom_command(\n      TARGET ${target_name}\n      POST_BUILD\n      COMMAND ${CMAKE_COMMAND} -E env ${CODESIGN} -f -s -\n              $<TARGET_FILE:${target_name}>)\n  endmacro()\nelse()\n  # Define a dummy code signature macro\n  macro(ADD_SIGNATURE target_name)\n\n  endmacro()\nendif()\n\n# Add tests\nif(QBDI_TEST OR QBDI_BENCHMARK)\n  enable_testing()\n  add_subdirectory(test)\nendif()\n\n# Add tools\nadd_subdirectory(tools)\n\n# Add examples\nif(QBDI_EXAMPLES)\n  add_subdirectory(examples)\nendif()\n\n# Add documentation\nif(QBDI_INCLUDE_DOCS)\n  add_subdirectory(docs)\nendif()\n\n# Add packaging\nif(QBDI_INCLUDE_PACKAGE)\n  add_subdirectory(package)\nendif()\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "################################################################################\nQBDI package license\n################################################################################\n\nQBDI binaries and packages can be statically linked with the followed source:\n\n- QBDI under the Apache License v2.0\n- LLVM under the Apache License v2.0 with LLVM Exceptions\n    https://releases.llvm.org/10.0.0/LICENSE.TXT\n- spdlog under the MIT License\n    https://github.com/gabime/spdlog/blob/v1.x/LICENSE\n- fmtlib under the MIT License\n    https://github.com/fmtlib/fmt/blob/master/LICENSE.rst\n- Catch2 under the Boost Software License 1.0\n    https://github.com/catchorg/Catch2/blob/devel/LICENSE.txt\n- Pybind11 under a BSD-style license\n    https://github.com/pybind/pybind11/blob/master/LICENSE\n- A sha256 implementation under GPLv3 / Apache License v2.0\n    https://github.com/aguinet/sha256_literal#licensing\n\n################################################################################\nQBDI source code is under the Apache License v2.0\n################################################################################\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017 - 2025 Quarkslab\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "\n## include any cmake file\n\ninclude CMakeLists.txt\nrecursive-include cmake *\nrecursive-exclude cmake config/*\n\n## include tools\n\ninclude tools/CMakeLists.txt\nrecursive-include tools/pyqbdi *\nrecursive-include tools/QBDIPreload *\n\n## include code\n\nrecursive-include src *\nrecursive-include include *\n\ninclude README-pypi.rst\n"
  },
  {
    "path": "README-pypi.rst",
    "content": "Introduction\n============\n\n.. image:: https://readthedocs.org/projects/qbdi/badge/?version=stable\n    :target: https://qbdi.readthedocs.io/en/stable/?badge=stable\n    :alt: Documentation Status\n\n.. image:: https://img.shields.io/github/v/release/QBDI/QBDI\n    :target: https://github.com/QBDI/QBDI/releases\n\n.. image:: https://img.shields.io/pypi/pyversions/PyQBDI\n    :target: https://pypi.org/project/PyQBDI/\n\n.. image:: https://img.shields.io/pypi/v/PyQBDI\n    :target: https://pypi.org/project/PyQBDI/\n\nQuarkslaB Dynamic binary Instrumentation (QBDI) is a modular, cross-platform and cross-architecture\nDBI framework. It aims to support Linux, macOS, Android, iOS and Windows operating systems running on\nx86, x86-64, ARM and AArch64 architectures. In addition of C/C++ API, Python and JS/frida bindings are\navailable to script QBDI. Information about what is a DBI framework and how QBDI\nworks can be found in the `documentation introduction <https://qbdi.readthedocs.io/en/stable/intro.html>`_.\n\nQBDI modularity means it doesn't contain a preferred injection method and it is designed to be\nused in conjunction with an external injection tool. QBDI includes a tiny (``LD_PRELOAD`` based)\nLinux and macOS injector for dynamic executables (QBDIPreload).\nQBDI is also fully integrated with `Frida <https://frida.re>`_, a reference dynamic instrumentation toolkit,\nallowing anybody to use their combined powers.\n\nA current limitation is that QBDI doesn't handle signals, multithreading (it doesn't deal with new\nthreads creation) and C++ exception mechanisms.\nHowever, those system-dependent features will probably not be part of the core library (KISS),\nand should be integrated as a new layer (to be determined how).\n\nStatus\n++++++\n\n.. role:: green\n.. role:: yellow\n.. role:: orange\n.. role:: red\n\n=======   ==============================   ==================   =========================\nCPU       Operating Systems                Execution            Memory Access Information\n=======   ==============================   ==================   =========================\nx86-64    Android, Linux, macOS, Windows   :green:`Supported`   :green:`Supported`\nx86       Android, Linux, macOS, Windows   :green:`Supported`   :green:`Supported`\nARM       Android, Linux                   :green:`Supported`   :green:`Supported`\nAArch64   Android, Linux, macOS, iOS (*)   :green:`Supported`   :green:`Supported`\n=======   ==============================   ==================   =========================\n\n\\* iOS support is unstable and need a jailbroken device.\n\nInstallation\n============\n\nPython API (PyQBDI)\n+++++++++++++++++++\n\nPyQBDI is available through PyPI. The wheel package can be either `downloaded <https://pypi.org/project/PyQBDI/#files>`__ or installed with the following command:\n\n    pip install PyQBDI\n\nThe PyQBDI package is self-contained so completely independent from the C/C++ package.\n\nDevel packages\n++++++++++++++\n\nThere is no strict development timeline or scheduled release plan for the QBDI project.\nAll the new features and fixes are merged onto the ``dev-next`` branch.\nDevel packages can be downloaded in the artefacts of `GitHub Actions <https://github.com/QBDI/QBDI/actions/workflows/python.yml?query=branch%3Adev-next>`__.\n\nCompilation\n===========\n\nThe PyQDBI library (apart from the wheel package) can be built by solely passing the **'-DQBDI_TOOLS_PYQBDI=ON'** option to the CMake build system.\n\nHowever, if you want to build the wheel package, you can run these commands::\n\n    git clone https://github.com/QBDI/QBDI.git\n    python -m pip install --upgrade pip\n    python -m pip install setuptools wheel build\n    python -m build -w\n\nA 32-bit version of Python is mandatory for the X86 architecture whereas a 64-bit one is required for the X86-64 architecture.\n"
  },
  {
    "path": "README.rst",
    "content": ".. image:: https://readthedocs.org/projects/qbdi/badge/?version=stable\n    :target: https://qbdi.readthedocs.io/en/stable/?badge=stable\n    :alt: Documentation Status\n\n.. image:: https://img.shields.io/github/v/release/QBDI/QBDI\n    :target: https://github.com/QBDI/QBDI/releases\n\n.. image:: https://img.shields.io/pypi/pyversions/PyQBDI\n    :target: https://pypi.org/project/PyQBDI/\n\n.. image:: https://img.shields.io/pypi/v/PyQBDI\n    :target: https://pypi.org/project/PyQBDI/\n\nIntroduction\n============\n.. intro\n\nQuarkslaB Dynamic binary Instrumentation (QBDI) is a modular, cross-platform and cross-architecture\nDBI framework. It aims to support Linux, macOS, Android, iOS and Windows operating systems running on\nx86, x86-64, ARM and AArch64 architectures. In addition of C/C++ API, Python and JS/frida bindings are\navailable to script QBDI. Information about what is a DBI framework and how QBDI\nworks can be found in the `documentation introduction <https://qbdi.readthedocs.io/en/stable/intro.html>`_.\n\nQBDI modularity means it doesn't contain a preferred injection method and it is designed to be\nused in conjunction with an external injection tool. QBDI includes a tiny (``LD_PRELOAD`` based)\nLinux and macOS injector for dynamic executables (QBDIPreload).\nQBDI is also fully integrated with `Frida <https://frida.re>`_, a reference dynamic instrumentation toolkit,\nallowing anybody to use their combined powers.\n\nA current limitation is that QBDI doesn't handle signals, multithreading (it doesn't deal with new\nthreads creation) and C++ exception mechanisms.\nHowever, those system-dependent features will probably not be part of the core library (KISS),\nand should be integrated as a new layer (to be determined how).\n\nStatus\n++++++\n\n.. role:: green\n.. role:: yellow\n.. role:: orange\n.. role:: red\n\n=======   ==============================   ==================   =========================\nCPU       Operating Systems                Execution            Memory Access Information\n=======   ==============================   ==================   =========================\nx86-64    Android, Linux, macOS, Windows   :green:`Supported`   :green:`Supported`\nx86       Android, Linux, macOS, Windows   :green:`Supported`   :green:`Supported`\nARM       Android, Linux                   :green:`Supported`   :green:`Supported`\nAArch64   Android, Linux, macOS, iOS (*)   :green:`Supported`   :green:`Supported`\n=======   ==============================   ==================   =========================\n\n\\* iOS support is unstable and need a jailbroken device.\n\n**stable**\n\n.. image:: https://readthedocs.org/projects/qbdi/badge/?version=stable\n    :target: https://qbdi.readthedocs.io/en/stable/\n    :alt: Documentation Status\n\n.. image:: https://github.com/QBDI/QBDI/actions/workflows/windows.yml/badge.svg?branch=master\n    :target: https://github.com/QBDI/QBDI/actions/workflows/windows.yml?query=branch%3Amaster\n\n.. image:: https://github.com/QBDI/QBDI/actions/workflows/linux.yml/badge.svg?branch=master\n    :target: https://github.com/QBDI/QBDI/actions/workflows/linux.yml?query=branch%3Amaster\n\n.. image:: https://github.com/QBDI/QBDI/actions/workflows/macos.yml/badge.svg?branch=master\n    :target: https://github.com/QBDI/QBDI/actions/workflows/macos.yml?query=branch%3Amaster\n\n**dev**\n\n.. image:: https://readthedocs.org/projects/qbdi/badge/?version=dev-next\n    :target: https://qbdi.readthedocs.io/en/dev-next/\n    :alt: Documentation Status\n\n.. image:: https://github.com/QBDI/QBDI/actions/workflows/windows.yml/badge.svg?branch=dev-next\n    :target: https://github.com/QBDI/QBDI/actions/workflows/windows.yml?query=branch%3Adev-next\n\n.. image:: https://github.com/QBDI/QBDI/actions/workflows/linux.yml/badge.svg?branch=dev-next\n    :target: https://github.com/QBDI/QBDI/actions/workflows/linux.yml?query=branch%3Adev-next\n\n.. image:: https://github.com/QBDI/QBDI/actions/workflows/macos.yml/badge.svg?branch=dev-next\n    :target: https://github.com/QBDI/QBDI/actions/workflows/macos.yml?query=branch%3Adev-next\n\n.. intro-end\n\nInstallation\n============\n\nC/C++/Frida APIs\n++++++++++++++++\n\nEvery new QBDI version is compiled and made available on the GitHub `release page <https://github.com/QBDI/QBDI/releases>`_.\n\nPython API (PyQBDI)\n+++++++++++++++++++\n\nPyQBDI is available through PyPI. The wheel package can be either `downloaded <https://pypi.org/project/PyQBDI/#files>`__ or installed with the following command:\n\n    pip install PyQBDI\n\nThe PyQBDI package is self-contained so completely independent from the C/C++ package.\n\nDevel packages\n++++++++++++++\n\nThere is no strict development timeline or scheduled release plan for the QBDI project.\nAll the new features and fixes are merged onto the ``dev-next`` branch.\nDevel packages can be downloaded in the artefacts of:\n\n- `GitHub Actions <https://github.com/QBDI/QBDI/actions/workflows/android.yml?query=branch%3Adev-next>`__ for Android C/C++/frida API\n- `GitHub Actions <https://github.com/QBDI/QBDI/actions/workflows/linux.yml?query=branch%3Adev-next>`__ for Linux C/C++/frida API (based on ubuntu)\n- `GitHub Actions <https://github.com/QBDI/QBDI/actions/workflows/macos.yml?query=branch%3Adev-next>`__ for macOS C/C++/frida API\n- `GitHub Actions <https://github.com/QBDI/QBDI/actions/workflows/windows.yml?query=branch%3Adev-next>`__ for Windows packages C/C++ API\n- `GitHub Actions <https://github.com/QBDI/QBDI/actions/workflows/python.yml?query=branch%3Adev-next>`__ for PyQBDI\n\nCompilation\n===========\n.. compil\n\nTo build this project, the following dependencies are needed on your system:\n\n- cmake >= 3.12\n- ninja or make\n- C++17 toolchain (gcc, clang, Visual Studio 2019, ...)\n\nA local version of llvm is statically built within QBDI because QBDI uses private APIs\nnot exported by regular LLVM installations and because our code is only compatible\nwith a specific version of those APIs.\n\nQBDI build system relies on CMake and requires to pass build configuration flags. To help with\nthis step we provide shell scripts for common build configurations which follow the naming pattern\n``config-OS-ARCH.sh``. Modifying these scripts is necessary if you want to compile in debug mode or\ncross-compile QBDI.\n\nLinux\n+++++\n\nx86-64\n^^^^^^\n\nCreate a new directory at the root of the source tree, and execute the Linux configuration script::\n\n    mkdir build\n    cd build\n    ../cmake/config/config-linux-X86_64.sh\n    ninja\n\nx86\n^^^\n\nYou can follow the same instructions as for x86-64 but instead, use the ``config-linux-X86.sh`` configuration script.\n\nmacOS\n+++++\n\nCompiling QBDI on macOS requires a few things:\n\n* A modern version of **macOS** (like Sierra)\n* **Xcode** (from the *App Store* or *Apple Developer Tools*)\n* the **Command Line Tools** (``xcode-select --install``)\n* a package manager (preferably **MacPorts**, but *HomeBrew* should also be fine)\n* some packages (``port install cmake wget ninja``)\n\nOnce requirements are met, create a new directory at the root of the source tree, and execute the macOS configuration script::\n\n    mkdir build\n    cd build\n    ../cmake/config/config-macOS-X86_64.sh\n    ninja\n\nWindows\n+++++++\n\nBuilding on Windows requires a pure Windows installation of *Python 3*\n(from the official packages, this is mandatory) in order to build our dependencies\n(we really hope to improve this in the future).\nIt also requires an up-to-date CMake and Ninja.\n\nFirst of all, the Visual Studio environment must be set up. This can be done with a command such as::\n\n    \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat\" x64\n\n\nThen, the following commands must be run::\n\n    mkdir build\n    cd build\n    python ../cmake/config/config-win-X86_64.py\n    ninja\n\nAndroid\n+++++++\n\nCross-compiling for Android requires the NDK (or the SDK) to be installed on your workstation.\nFor now, it has only been tested under Linux.\nIf not already installed, you can download the latest Android NDK package\nthrough the `official website <https://developer.android.com/ndk/downloads>`__\nand extract it.\nAfterwards, the ``config-android-*.sh`` configuration script needs to be\ncustomised to match your NDK installation directory and the target platform.::\n\n    # Configure and compile QBDI X86_64 with a NDK\n    mkdir build && cd build\n    NDK_PATH=<your_NDK_PATH> ../cmake/config/config-android-X86_64.sh\n    ninja\n\n    # Configure and compile QBDI X86 with a SDK\n    mkdir build && cd build\n    ANDROID_SDK_ROOT=<your_SDK_PATH> ../cmake/config/config-android-X86.sh\n    ninja\n\nPyQBDI compilation\n++++++++++++++++++\n\nThe PyQDBI library (apart from the wheel package) can be built by solely passing the **'-DQBDI_TOOLS_PYQBDI=ON'** option to the CMake build system.\n\nHowever, if you want to build the wheel package, you can run these commands::\n\n    python -m pip install --upgrade pip\n    python -m pip install setuptools wheel build\n    python -m build -w\n\nA 32-bit version of Python is mandatory for the X86 architecture whereas a 64-bit one is required for the X86-64 architecture.\n\n.. compil-end\n"
  },
  {
    "path": "cmake/QBDIConfig.cmake",
    "content": "if(__add_qbdi_config)\n  return()\nendif()\nset(__add_qbdi_config ON)\n\n# ========================\n# CMAKE build type\n# ========================\n\nset(DEFAULT_BUILD_TYPE Release)\n\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  message(\n    STATUS\n      \"Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.\")\n  set(CMAKE_BUILD_TYPE\n      \"${DEFAULT_BUILD_TYPE}\"\n      CACHE STRING \"Used default build type (${DEFAULT_BUILD_TYPE}).\" FORCE)\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS \"Release\" \"Debug\"\n                                               \"MinSizeRel\" \"RelWithDebInfo\")\nendif()\n\n# ========================\n# QBDI Version\n# ========================\n# note: the version should also be changed in the followed files:\n# - docker/archlinux/PKGBUILD.{X86|X86_64}\n# - docker/common.sh\n# - setup.cfg\n# - tools/frida-qbdi.js\nset(QBDI_VERSION_MAJOR 0)\nset(QBDI_VERSION_MINOR 12)\nset(QBDI_VERSION_PATCH 2)\nset(QBDI_VERSION_DEV 1)\n\nset(QBDI_VERSION_STRING\n    \"${QBDI_VERSION_MAJOR}.${QBDI_VERSION_MINOR}.${QBDI_VERSION_PATCH}\")\nif(QBDI_VERSION_DEV)\n  set(QBDI_VERSION_STRING \"${QBDI_VERSION_STRING}-devel\")\n  if(EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/.git\"\n     AND IS_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/.git\")\n    set_property(\n      DIRECTORY\n      APPEND\n      PROPERTY CMAKE_CONFIGURE_DEPENDS \"${CMAKE_CURRENT_SOURCE_DIR}/.git\")\n    find_package(Git REQUIRED)\n    execute_process(\n      COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD\n      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n      OUTPUT_VARIABLE COMMIT_HASH\n      OUTPUT_STRIP_TRAILING_WHITESPACE)\n    set(QBDI_VERSION_FULL_STRING \"${QBDI_VERSION_STRING}-${COMMIT_HASH}\")\n  elseif(DEFINED ENV{APPVEYOR_REPO_COMMIT})\n    set(QBDI_VERSION_FULL_STRING\n        \"${QBDI_VERSION_STRING}-$ENV{APPVEYOR_REPO_COMMIT}\")\n  else()\n    set(QBDI_VERSION_FULL_STRING \"${QBDI_VERSION_STRING}\")\n  endif()\nelse()\n  set(QBDI_VERSION_FULL_STRING \"${QBDI_VERSION_STRING}\")\nendif()\n\n# ========================\n# QBDI Platform\n# ========================\nset(QBDI_SUPPORTED_PLATFORMS \"android\" \"linux\" \"windows\" \"macos\" \"ios\")\n\nset(QBDI_SUPPORTED_ARCH \"ARM\" \"AARCH64\" \"X86\" \"X86_64\")\n\nset(QBDI_PLATFORM_WINDOWS 0)\nset(QBDI_PLATFORM_LINUX 0)\nset(QBDI_PLATFORM_ANDROID 0)\nset(QBDI_PLATFORM_MACOS 0)\nset(QBDI_PLATFORM_IOS 0)\n\nset(QBDI_ARCH_X86 0)\nset(QBDI_ARCH_X86_64 0)\nset(QBDI_ARCH_ARM 0)\nset(QBDI_ARCH_AARCH64 0)\n\nset(QBDI_BITS_32 0)\nset(QBDI_BITS_64 0)\n\n# Check platform\nif(NOT DEFINED QBDI_PLATFORM)\n  message(\n    FATAL_ERROR\n      \"Please set 'QBDI_PLATFORM'\\nSupported platform: ${QBDI_SUPPORTED_PLATFORMS}\"\n  )\nelseif(QBDI_PLATFORM STREQUAL \"windows\")\n  set(QBDI_PLATFORM_WINDOWS 1)\nelseif(QBDI_PLATFORM STREQUAL \"linux\")\n  set(QBDI_PLATFORM_LINUX 1)\nelseif(QBDI_PLATFORM STREQUAL \"android\")\n  set(QBDI_PLATFORM_ANDROID 1)\nelseif(QBDI_PLATFORM STREQUAL \"osx\")\n  set(QBDI_PLATFORM_MACOS 1)\nelseif(QBDI_PLATFORM STREQUAL \"macos\")\n  set(QBDI_PLATFORM_MACOS 1)\nelseif(QBDI_PLATFORM STREQUAL \"ios\")\n  set(QBDI_PLATFORM_IOS 1)\nelse()\n  message(\n    FATAL_ERROR\n      \"${QBDI_PLATFORM} is not a supported platform.\\nCurrently supported: ${QBDI_SUPPORTED_PLATFORMS}\"\n  )\nendif()\n\n# Check arch\nif(NOT DEFINED QBDI_ARCH)\n  message(\n    FATAL_ERROR\n      \"Please set 'QBDI_ARCH'\\nSupported architecture: ${QBDI_SUPPORTED_ARCH}\")\nelseif(QBDI_ARCH STREQUAL \"ARM\")\n  set(QBDI_ARCH_ARM 1)\n  set(QBDI_BITS_32 1)\n  set(QBDI_LLVM_ARCH \"ARM\")\n  set(QBDI_BASE_ARCH \"ARM\")\nelseif(QBDI_ARCH STREQUAL \"AARCH64\")\n  set(QBDI_ARCH_AARCH64 1)\n  set(QBDI_BITS_64 1)\n  set(QBDI_LLVM_ARCH \"AArch64\")\n  set(QBDI_BASE_ARCH \"AARCH64\")\nelseif(QBDI_ARCH STREQUAL \"X86\")\n  set(QBDI_ARCH_X86 1)\n  set(QBDI_BITS_32 1)\n  set(QBDI_LLVM_ARCH \"X86\")\n  set(QBDI_BASE_ARCH \"X86_64\")\nelseif(QBDI_ARCH STREQUAL \"X86_64\")\n  set(QBDI_ARCH_X86_64 1)\n  set(QBDI_BITS_64 1)\n  set(QBDI_LLVM_ARCH \"X86\")\n  set(QBDI_BASE_ARCH \"X86_64\")\nelse()\n  message(\n    FATAL_ERROR\n      \"${QBDI_ARCH} is not a supported architecture.\\nCurrently supported: ${QBDI_SUPPORTED_ARCH}\"\n  )\nendif()\n\nif(QBDI_PLATFORM_WINDOWS AND NOT (QBDI_ARCH_X86 OR QBDI_ARCH_X86_64))\n  message(\n    FATAL_ERROR\n      \"Windows platform is only supported for X86 and X86_64 architectures.\")\nendif()\n\nif(QBDI_PLATFORM_MACOS\n   AND NOT\n       (QBDI_ARCH_X86\n        OR QBDI_ARCH_X86_64\n        OR QBDI_ARCH_AARCH64))\n  message(\n    FATAL_ERROR\n      \"OSX platform is only supported for X86 and X86_64 architectures.\")\nendif()\n\nif(QBDI_PLATFORM_IOS AND NOT (QBDI_ARCH_ARM OR QBDI_ARCH_AARCH64))\n  message(\n    FATAL_ERROR\n      \"IOS platform is only supported for ARM and AARCH64 architecture.\")\nendif()\n\nif(QBDI_PLATFORM_MACOS)\n  if(QBDI_ARCH_X86_64 AND NOT (\"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL\n                               \"x86_64\"))\n    set(CMAKE_OSX_ARCHITECTURES\n        \"x86_64\"\n        CACHE STRING \"\" FORCE)\n    set(CMAKE_CROSSCOMPILING\n        \"TRUE\"\n        CACHE STRING \"\" FORCE)\n  elseif(QBDI_ARCH_AARCH64 AND NOT (\"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL\n                                    \"arm64\"))\n    set(CMAKE_OSX_ARCHITECTURES\n        \"arm64\"\n        CACHE STRING \"\" FORCE)\n    set(CMAKE_CROSSCOMPILING\n        \"TRUE\"\n        CACHE STRING \"\" FORCE)\n  endif()\nendif()\n\nif(QBDI_PTRAUTH AND NOT ((QBDI_PLATFORM_MACOS OR QBDI_PLATFORM_IOS)\n                         AND QBDI_ARCH_AARCH64))\n  message(\n    FATAL_ERROR\n      \"PTRAUTH is only supported for iOS and macOS platforms and AARCH64 architecture.\"\n  )\nendif()\n\nmessage(STATUS \"\")\nmessage(STATUS \"== QBDI Target ==\")\nmessage(STATUS \"QBDI Platform:      ${QBDI_PLATFORM}\")\nmessage(STATUS \"QBDI ARCH:          ${QBDI_ARCH}\")\nmessage(STATUS \"LLVM ARCH:          ${QBDI_LLVM_ARCH}\")\nmessage(STATUS \"Version:            ${QBDI_VERSION_FULL_STRING}\")\nmessage(STATUS \"\")\n"
  },
  {
    "path": "cmake/QBDIDependencies.cmake",
    "content": "if(__add_qbdi_deps)\n  return()\nendif()\nset(__add_qbdi_deps ON)\ninclude(FetchContent)\n\nif(NOT DEFINED QBDI_THIRD_PARTY_DIRECTORY)\n  set(QBDI_THIRD_PARTY_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/third-party/\")\nendif()\nset(qbdi_deps)\n\n# Custom FetchContent Command\n# ===========================\n\n# since CMAKE 3.30, FetchContent_Declare doesn't support DOWNLOAD_DIR, which is\n# used in order to cache the downloaded archive during CI.\n# The followed function are used to keep this functionnality\n\nfunction(DOWNLOAD_PACKAGE PACKAGE_NAME)\n\n  string(TOLOWER \"${PACKAGE_NAME}\" PACKAGE_NAME_LOWER)\n  set(FetchName \"qbdi_deps_${PACKAGE_NAME_LOWER}\")\n\n  FetchContent_Populate(\n    ${FetchName}\n    DOWNLOAD_DIR \"${QBDI_THIRD_PARTY_DIRECTORY}/${PACKAGE_NAME}_download\"\n    SOURCE_DIR \"${FETCHCONTENT_BASE_DIR}/${PACKAGE_NAME_LOWER}-src\"\n    BINARY_DIR \"${FETCHCONTENT_BASE_DIR}/${PACKAGE_NAME_LOWER}-build\"\n    SUBBUILD_DIR \"${FETCHCONTENT_BASE_DIR}/${PACKAGE_NAME_LOWER}-subbuild\"\n    QUIET ${ARGN})\n\n  set(\"${PACKAGE_NAME}_EXTRACT_PATH\"\n      \"${${FetchName}_SOURCE_DIR}\"\n      PARENT_SCOPE)\nendfunction()\n\nfunction(PREPARE_PACKAGE PACKAGE_NAME)\n\n  cmake_parse_arguments(ARG \"\" \"URL;URL_HASH\" \"\" ${ARGN})\n\n  if(NOT ((DEFINED ARG_URL) AND (DEFINED ARG_URL_HASH)))\n    message(FATAL_ERROR \"missing URL or URL_HASH in function argument\")\n  endif()\n\n  download_package(${PACKAGE_NAME} URL \"${ARG_URL}\" URL_HASH \"${ARG_URL_HASH}\")\n\n  FetchContent_Declare(\n    ${PACKAGE_NAME} SOURCE_DIR \"${${PACKAGE_NAME}_EXTRACT_PATH}\"\n                    ${ARG_UNPARSED_ARGUMENTS})\nendfunction()\n\n# Spdlog\n# ======\nset(SPDLOG_VERSION 1.15.0)\n\nprepare_package(\n  spdlog\n  URL\n  \"https://github.com/gabime/spdlog/archive/v${SPDLOG_VERSION}.zip\"\n  URL_HASH\n  \"SHA256=076f3b4d452b95433083bcc66d07f79addba2d3fcb2b9177abeb7367d47aefbb\"\n  EXCLUDE_FROM_ALL)\n\nlist(APPEND qbdi_deps spdlog)\n\nset(SPDLOG_NO_EXCEPTIONS\n    ON\n    CACHE BOOL \"Enable SPDLOG_NO_EXCEPTIONS\")\nset(SPDLOG_NO_TLS\n    ON\n    CACHE BOOL \"Enable SPDLOG_NO_TLS\")\nset(SPDLOG_NO_THREAD_ID\n    ON\n    CACHE BOOL \"Enable SPDLOG_NO_THREAD_ID\")\nif(QBDI_LOG_DEBUG)\n  set(SPDLOG_ACTIVE_LEVEL\n      SPDLOG_LEVEL_DEBUG\n      CACHE BOOL \"Enable SPDLOG_LEVEL_DEBUG level\")\nendif()\n\n# Catch2\n# ======\nif((QBDI_TEST) OR (QBDI_BENCHMARK))\n  prepare_package(\n    Catch2\n    URL\n    \"https://github.com/catchorg/Catch2/archive/refs/tags/v3.3.1.zip\"\n    URL_HASH\n    \"SHA256=5e5283bf93b2693f6877bba3eaa76d66588955374d0cec5b40117066c044ad5e\"\n    EXCLUDE_FROM_ALL)\n  list(APPEND qbdi_deps Catch2)\nendif()\n\n# Pybind11\n# ========\nif(QBDI_TOOLS_PYQBDI)\n  if(DEFINED QBDI_TOOLS_PYQBDI_TARGET_PYTHON_VERSION)\n    find_package(\n      Python3 \"${QBDI_TOOLS_PYQBDI_TARGET_PYTHON_VERSION}\" EXACT REQUIRED\n      COMPONENTS Interpreter Development.Module\n      OPTIONAL_COMPONENTS Development.Embed)\n  else()\n    find_package(\n      Python3 REQUIRED\n      COMPONENTS Interpreter Development.Module\n      OPTIONAL_COMPONENTS Development.Embed)\n  endif()\n\n  if(NOT Python3_FOUND)\n    message(FATAL_ERROR \"Python not found\")\n  endif()\n\n  set(PYBIND11_FINDPYTHON ON)\n\n  prepare_package(\n    pybind11\n    URL\n    \"https://github.com/pybind/pybind11/archive/v3.0.1.zip\"\n    URL_HASH\n    \"SHA256=20fb420fe163d0657a262a8decb619b7c3101ea91db35f1a7227e67c426d4c7e\"\n    EXCLUDE_FROM_ALL)\n  list(APPEND qbdi_deps pybind11)\nendif()\n\n# Fetch all deps\n# ==============\nFetchContent_MakeAvailable(${qbdi_deps})\n\n# Fetch LLVM\n# ==========\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/llvm\")\ninclude(QBDI_llvm)\n\n# Spdlog Configure\n# ================\nset_property(TARGET spdlog PROPERTY POSITION_INDEPENDENT_CODE ON)\ntarget_compile_definitions(\n  spdlog INTERFACE SPDLOG_NO_TLS=1 SPDLOG_NO_THREAD_ID=1 SPDLOG_NO_EXCEPTIONS=1)\n\nif(QBDI_LOG_DEBUG)\n  target_compile_definitions(spdlog\n                             INTERFACE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG)\nelse()\n  target_compile_definitions(spdlog\n                             INTERFACE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO)\nendif()\n\n# remove Threads::Threads for android\nif(ANDROID)\n  get_target_property(SPDLOG_LIB spdlog INTERFACE_LINK_LIBRARIES)\n  list(REMOVE_ITEM SPDLOG_LIB Threads::Threads)\n  set_property(TARGET spdlog PROPERTY INTERFACE_LINK_LIBRARIES \"${SPDLOG_LIB}\")\nendif()\n\nif(QBDI_PLATFORM_WINDOWS)\n  target_compile_definitions(spdlog PUBLIC _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)\n\nelse()\n  set(SPDLOG_QBDI_CXX_FLAGS\n      -ffunction-sections -fdata-sections -fvisibility-inlines-hidden\n      -fvisibility=hidden -fPIC -fno-rtti)\n  target_compile_options(\n    spdlog PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${SPDLOG_QBDI_CXX_FLAGS}>)\nendif()\n"
  },
  {
    "path": "cmake/QBDIOptions.cmake",
    "content": "if(__add_qbdi_options)\n  return()\nendif()\nset(__add_qbdi_options ON)\n\n# Enable compilation using ccache\noption(QBDI_CCACHE \"Enable CCACHE or SCCACHE\" ON)\n\n# FORCE_DISABLE_AVX - default is OFF\nif(QBDI_ARCH_X86_64 OR QBDI_ARCH_X86)\n  option(QBDI_DISABLE_AVX\n         \"Force disable AVX support in case dynamic support detection is buggy\"\n         OFF)\nelse()\n  # AVX only available on X86 and X86-64\n  set(QBDI_DISABLE_AVX OFF)\nendif()\n\n# ASAN option\noption(QBDI_ASAN\n       \"Enable AddressSanitizer (ASAN) for debugging (May be slow down)\" OFF)\n\n# Enable the logging level debug\noption(QBDI_LOG_DEBUG \"Enable Debug log level\" OFF)\n\n# Compile static Library\noption(QBDI_STATIC_LIBRARY \"Build the static library\" ON)\n\n# Compile shared Library\noption(QBDI_SHARED_LIBRARY \"Build the shared library\" ON)\n\nif(NOT QBDI_PLATFORM_ANDROID)\n  # test\n  option(QBDI_TEST \"Compile tests\" ON)\n\n  # benchmark\n  option(QBDI_BENCHMARK \"Compile benchmark\" OFF)\nelse()\n  set(QBDI_TEST OFF)\n  set(QBDI_BENCHMARK OFF)\nendif()\n\n# example\noption(QBDI_EXAMPLES \"Compile examples\" OFF)\n\n# docs\noption(QBDI_INCLUDE_DOCS \"Include cmake docs\" ON)\n\n# package\noption(QBDI_INCLUDE_PACKAGE \"Include cmake package (for cpack)\" ON)\n\n# package\noption(QBDI_INSTALL \"Prepare install target\" ON)\n\n# tools\nif(NOT (QBDI_PLATFORM_IOS OR QBDI_PLATFORM_ANDROID))\n\n  option(QBDI_TOOLS_QBDIPRELOAD \"Compile QBDIPRELOAD\" ON)\n\n  if(QBDI_PLATFORM_WINDOWS)\n    set(QBDI_TOOLS_VALIDATOR OFF)\n  else()\n    # Validator (compare execution between QBDIPreload and ptrace)\n    option(QBDI_TOOLS_VALIDATOR\n           \"Compile the validator (need QBDI_TOOLS_QBDIPRELOAD)\" OFF)\n  endif()\nelse()\n  set(QBDI_TOOLS_QBDIPRELOAD OFF)\n  set(QBDI_TOOLS_VALIDATOR OFF)\nendif()\n\n# PYQBDI (need a python 32bit for 32bit architecture)\nif(QBDI_BITS_64\n   AND (QBDI_PLATFORM_WINDOWS\n        OR QBDI_PLATFORM_LINUX\n        OR QBDI_PLATFORM_MACOS))\n  option(QBDI_TOOLS_PYQBDI \"Compile python binding\" ON)\nelse()\n  option(QBDI_TOOLS_PYQBDI \"Compile python binding\" OFF)\nendif()\n\n# binding QBDI for frida\noption(QBDI_TOOLS_FRIDAQBDI \"Install frida-qbdi\" ON)\n\n# verify options\nif(NOT QBDI_STATIC_LIBRARY)\n  if(QBDI_TEST)\n    message(FATAL_ERROR \"Need QBDI_STATIC_LIBRARY to compile QBDI_TEST\")\n  endif()\n  if(QBDI_BENCHMARK)\n    message(FATAL_ERROR \"Need QBDI_STATIC_LIBRARY to compile QBDI_BENCHMARK\")\n  endif()\n  if(QBDI_TOOLS_PYQBDI)\n    message(FATAL_ERROR \"Need QBDI_STATIC_LIBRARY to compile QBDI_TOOLS_PYQBDI\")\n  endif()\n  if(QBDI_TOOLS_QBDIPRELOAD)\n    message(\n      FATAL_ERROR \"Need QBDI_STATIC_LIBRARY to compile QBDI_TOOLS_QBDIPRELOAD\")\n  endif()\n  if(QBDI_TOOLS_VALIDATOR)\n    message(\n      FATAL_ERROR \"Need QBDI_STATIC_LIBRARY to compile QBDI_TOOLS_VALIDATOR\")\n  endif()\nendif()\n\nif(NOT QBDI_SHARED_LIBRARY)\n  if(QBDI_EXAMPLES)\n    message(FATAL_ERROR \"Need QBDI_SHARED_LIBRARY to compile QBDI_EXAMPLES\")\n  endif()\nendif()\n\nif(QBDI_TOOLS_VALIDATOR AND NOT QBDI_TOOLS_QBDIPRELOAD)\n  message(\n    FATAL_ERROR \"Need QBDI_TOOLS_QBDIPRELOAD to compile QBDI_TOOLS_VALIDATOR\")\nendif()\n\n# display resulted options\nmessage(STATUS \"== QBDI Options ==\")\nmessage(STATUS \"QBDI_CCACHE:           ${QBDI_CCACHE}\")\n\nif(QBDI_ARCH_X86_64 OR QBDI_ARCH_X86)\n  message(STATUS \"QBDI_DISABLE_AVX:      ${QBDI_DISABLE_AVX}\")\nendif()\n\nmessage(STATUS \"QBDI_ASAN:             ${QBDI_ASAN}\")\nif(CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n  set(QBDI_LOG_DEBUG ON)\n  message(\n    STATUS\n      \"QBDI_LOG_DEBUG:        ${QBDI_LOG_DEBUG} (by CMAKE_BUILD_TYPE=Debug)\")\nelse()\n  message(STATUS \"QBDI_LOG_DEBUG:        ${QBDI_LOG_DEBUG}\")\nendif()\nmessage(STATUS \"QBDI_STATIC_LIBRARY:   ${QBDI_STATIC_LIBRARY}\")\nmessage(STATUS \"QBDI_SHARED_LIBRARY:   ${QBDI_SHARED_LIBRARY}\")\nmessage(STATUS \"QBDI_TEST:             ${QBDI_TEST}\")\nmessage(STATUS \"QBDI_BENCHMARK:        ${QBDI_BENCHMARK}\")\nmessage(STATUS \"QBDI_EXAMPLES:         ${QBDI_EXAMPLES}\")\nmessage(STATUS \"QBDI_TOOLS_QBDIPRELOAD: ${QBDI_TOOLS_QBDIPRELOAD}\")\nmessage(STATUS \"QBDI_TOOLS_VALIDATOR:  ${QBDI_TOOLS_VALIDATOR}\")\nmessage(STATUS \"QBDI_TOOLS_PYQBDI:     ${QBDI_TOOLS_PYQBDI}\")\nmessage(STATUS \"QBDI_TOOLS_FRIDAQBDI:  ${QBDI_TOOLS_FRIDAQBDI}\")\n\nmessage(STATUS \"\")\n\nif(QBDI_LOG_DEBUG)\n  set(QBDI_ENABLE_LOG_DEBUG 1)\nelse()\n  set(QBDI_ENABLE_LOG_DEBUG 0)\nendif()\n"
  },
  {
    "path": "cmake/config/config-android-X86.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\nif [ -z \"${NDK_PATH}\" ]; then\n  if [ -z \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"ANDROID_SDK_ROOT or NDK_PATH variable should be set to configure cmake.\"\n    exit 1\n  fi\n  if [ ! -d \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"'${ANDROID_SDK_ROOT}' is not valid!\"\n    exit 1\n  fi\n  export NDK_PATH=\"${ANDROID_SDK_ROOT}/ndk-bundle/\"\nfi\n\nif [ ! -d \"${NDK_PATH}\" ]; then\n  echo \"'${NDK_PATH}' is not valid!\"\n  exit 1\nfi\n\ncmake \"${GITDIR}\"                        \\\n      -DQBDI_PLATFORM=android            \\\n      -DQBDI_ARCH=X86                    \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_TOOLCHAIN_FILE=\"${NDK_PATH}/build/cmake/android.toolchain.cmake\" \\\n      -DANDROID_ABI=x86                  \\\n      -DANDROID_PLATFORM=23              \\\n      -G Ninja\n\n\n"
  },
  {
    "path": "cmake/config/config-android-X86_64.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\nif [ -z \"${NDK_PATH}\" ]; then\n  if [ -z \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"ANDROID_SDK_ROOT or NDK_PATH variable should be set to configure cmake.\"\n    exit 1\n  fi\n  if [ ! -d \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"'${ANDROID_SDK_ROOT}' is not valid!\"\n    exit 1\n  fi\n  export NDK_PATH=\"${ANDROID_SDK_ROOT}/ndk-bundle/\"\nfi\n\nif [ ! -d \"${NDK_PATH}\" ]; then\n  echo \"'${NDK_PATH}' is not valid!\"\n  exit 1\nfi\n\ncmake \"${GITDIR}\"                        \\\n      -DQBDI_PLATFORM=android            \\\n      -DQBDI_ARCH=X86_64                 \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_TOOLCHAIN_FILE=\"${NDK_PATH}/build/cmake/android.toolchain.cmake\" \\\n      -DANDROID_ABI=x86_64               \\\n      -DANDROID_PLATFORM=23              \\\n      -G Ninja\n\n\n"
  },
  {
    "path": "cmake/config/config-android-aarch64.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\nif [ -z \"${NDK_PATH}\" ]; then\n  if [ -z \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"ANDROID_SDK_ROOT or NDK_PATH variable should be set to configure cmake.\"\n    exit 1\n  fi\n  if [ ! -d \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"'${ANDROID_SDK_ROOT}' is not valid!\"\n    exit 1\n  fi\n  export NDK_PATH=\"${ANDROID_SDK_ROOT}/ndk-bundle/\"\nfi\n\nif [ ! -d \"${NDK_PATH}\" ]; then\n  echo \"'${NDK_PATH}' is not valid!\"\n  exit 1\nfi\n\ncmake \"${GITDIR}\"                        \\\n      -DQBDI_PLATFORM=android            \\\n      -DQBDI_ARCH=AARCH64                \\\n      -DCMAKE_BUILD_TYPE=RelWithDebInfo  \\\n      -DCMAKE_TOOLCHAIN_FILE=\"${NDK_PATH}/build/cmake/android.toolchain.cmake\" \\\n      -DANDROID_ABI=\"arm64-v8a\"          \\\n      -DANDROID_PLATFORM=24              \\\n      -G Ninja\n\n"
  },
  {
    "path": "cmake/config/config-android-armv7.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\nif [ -z \"${NDK_PATH}\" ]; then\n  if [ -z \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"ANDROID_SDK_ROOT or NDK_PATH variable should be set to configure cmake.\"\n    exit 1\n  fi\n  if [ ! -d \"${ANDROID_SDK_ROOT}\" ]; then\n    echo \"'${ANDROID_SDK_ROOT}' is not valid!\"\n    exit 1\n  fi\n  export NDK_PATH=\"${ANDROID_SDK_ROOT}/ndk-bundle/\"\nfi\n\nif [ ! -d \"${NDK_PATH}\" ]; then\n  echo \"'${NDK_PATH}' is not valid!\"\n  exit 1\nfi\n\ncmake \"${GITDIR}\"                        \\\n      -DQBDI_PLATFORM=android            \\\n      -DQBDI_ARCH=ARM                    \\\n      -DCMAKE_BUILD_TYPE=RelWithDebInfo  \\\n      -DCMAKE_TOOLCHAIN_FILE=\"${NDK_PATH}/build/cmake/android.toolchain.cmake\" \\\n      -DANDROID_ABI=\"armeabi-v7a\"        \\\n      -DANDROID_PLATFORM=24              \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-ios-aarch64.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DQBDI_PLATFORM=ios                \\\n      -DQBDI_ARCH=AARCH64                \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_TOOLCHAIN_FILE=\"${GITDIR}/cmake/config/ios.toolchain.cmake\" \\\n      -DCMAKE_OSX_DEPLOYMENT_TARGET=\"15.0\" \\\n      -G Ninja\n\n"
  },
  {
    "path": "cmake/config/config-linux-X86.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_CROSSCOMPILING=FALSE       \\\n      -DQBDI_PLATFORM=linux              \\\n      -DQBDI_ARCH=X86                    \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-linux-X86_64.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_CROSSCOMPILING=FALSE       \\\n      -DQBDI_PLATFORM=linux              \\\n      -DQBDI_ARCH=X86_64                 \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-linux-aarch64.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_CROSSCOMPILING=FALSE       \\\n      -DQBDI_PLATFORM=linux              \\\n      -DQBDI_ARCH=X86_64                 \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-macOS-AARCH64.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_CROSSCOMPILING=FALSE       \\\n      -DQBDI_PLATFORM=macos              \\\n      -DQBDI_ARCH=AARCH64                \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-macOS-AARCH64e.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_CROSSCOMPILING=FALSE       \\\n      -DQBDI_PLATFORM=macos              \\\n      -DQBDI_ARCH=AARCH64                \\\n      -DCMAKE_OSX_ARCHITECTURES=\"arm64e\" \\\n      -DQBDI_PTRAUTH=ON                  \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-macOS-X86.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_CROSSCOMPILING=FALSE       \\\n      -DQBDI_PLATFORM=macos              \\\n      -DQBDI_ARCH=X86                    \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-macOS-X86_64.sh",
    "content": "#!/bin/sh\nset -e\n\nBASEDIR=\"$(cd \"$(dirname \"$0\")\" && pwd -P)\"\nGITDIR=\"$(realpath \"${BASEDIR}/../..\")\"\n\ncmake \"${GITDIR}\"                        \\\n      -DCMAKE_BUILD_TYPE=Release         \\\n      -DCMAKE_CROSSCOMPILING=FALSE       \\\n      -DQBDI_PLATFORM=macos              \\\n      -DQBDI_ARCH=X86_64                 \\\n      -G Ninja\n"
  },
  {
    "path": "cmake/config/config-win-X86.py",
    "content": "import subprocess\nimport os\n\n# be sure to run \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat\" x86 before\n\nsubprocess.check_call([\"cmake\", os.path.realpath(os.path.join(os.path.dirname(__file__), \"..\" , \"..\")),\n                       \"-G\", \"Ninja\",\n                       \"-DCMAKE_CXX_FLAGS=/D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING\",\n                       \"-DCMAKE_BUILD_TYPE=Release\",\n                       \"-DCMAKE_CROSSCOMPILING=FALSE\",\n                       \"-DQBDI_PLATFORM=windows\",\n                       \"-DQBDI_ARCH=X86\"])\n"
  },
  {
    "path": "cmake/config/config-win-X86_64.py",
    "content": "import subprocess\nimport os\n\n# be sure to run \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat\" x64 before\n\nsubprocess.check_call([\"cmake\", os.path.realpath(os.path.join(os.path.dirname(__file__), \"..\" , \"..\")),\n                       \"-G\", \"Ninja\",\n                       \"-DCMAKE_BUILD_TYPE=Release\",\n                       \"-DCMAKE_CROSSCOMPILING=FALSE\",\n                       \"-DQBDI_PLATFORM=windows\",\n                       \"-DQBDI_ARCH=X86_64\"])\n"
  },
  {
    "path": "cmake/config/ios.toolchain.cmake",
    "content": "# from https://github.com/llvm/llvm-project/blob/release/19.x/llvm/cmake/platforms/iOS.cmake\n\n# LICENSE : Apache License v2.0 with LLVM Exceptions\n# see https://github.com/llvm/llvm-project/blob/release/19.x/LICENSE.TXT\n\n# Toolchain config for iOS.\n\nSET(CMAKE_SYSTEM_NAME Darwin)\nSET(CMAKE_SYSTEM_VERSION 13)\nSET(CMAKE_CXX_COMPILER_WORKS True)\nSET(CMAKE_C_COMPILER_WORKS True)\nSET(IOS True)\n\nif(NOT CMAKE_OSX_SYSROOT)\n  execute_process(COMMAND xcodebuild -version -sdk iphoneos Path\n    OUTPUT_VARIABLE SDKROOT\n    ERROR_QUIET\n    OUTPUT_STRIP_TRAILING_WHITESPACE)\n\n  IF(NOT EXISTS ${SDKROOT})\n    MESSAGE(FATAL_ERROR \"SDKROOT could not be detected!\")\n  ENDIF()\n\n  message(STATUS \"Using SDKROOT ${SDKROOT}\")\n  set(CMAKE_OSX_SYSROOT ${SDKROOT})\nendif()\n\nIF(NOT CMAKE_C_COMPILER)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang\n   OUTPUT_VARIABLE CMAKE_C_COMPILER\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  message(STATUS \"Using C compiler ${CMAKE_C_COMPILER}\")\nENDIF()\n\nIF(NOT CMAKE_CXX_COMPILER)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang++\n   OUTPUT_VARIABLE CMAKE_CXX_COMPILER\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  message(STATUS \"Using C++ compiler ${CMAKE_CXX_COMPILER}\")\nENDIF()\n\nIF(NOT CMAKE_AR)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find ar\n   OUTPUT_VARIABLE CMAKE_AR_val\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  SET(CMAKE_AR ${CMAKE_AR_val} CACHE FILEPATH \"Archiver\")\n  message(STATUS \"Using ar ${CMAKE_AR}\")\nENDIF()\n\nIF(NOT CMAKE_RANLIB)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find ranlib\n   OUTPUT_VARIABLE CMAKE_RANLIB_val\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  SET(CMAKE_RANLIB ${CMAKE_RANLIB_val} CACHE FILEPATH \"Ranlib\")\n  message(STATUS \"Using ranlib ${CMAKE_RANLIB}\")\nENDIF()\n\nIF(NOT CMAKE_STRIP)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find strip\n   OUTPUT_VARIABLE CMAKE_STRIP_val\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  SET(CMAKE_STRIP ${CMAKE_STRIP_val} CACHE FILEPATH \"Strip\")\n  message(STATUS \"Using strip ${CMAKE_STRIP}\")\nENDIF()\n\nIF(NOT CMAKE_DSYMUTIL)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find dsymutil\n   OUTPUT_VARIABLE CMAKE_DSYMUTIL_val\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  SET(CMAKE_DSYMUTIL ${CMAKE_DSYMUTIL_val} CACHE FILEPATH \"Dsymutil\")\n  message(STATUS \"Using dsymutil ${CMAKE_DSYMUTIL}\")\nENDIF()\n\nIF(NOT CMAKE_LIBTOOL)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find libtool\n   OUTPUT_VARIABLE CMAKE_LIBTOOL_val\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  SET(CMAKE_LIBTOOL ${CMAKE_LIBTOOL_val} CACHE FILEPATH \"Libtool\")\n  message(STATUS \"Using libtool ${CMAKE_LIBTOOL}\")\nENDIF()\n\nIF(NOT CMAKE_CODESIGN)\n  execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find codesign\n   OUTPUT_VARIABLE CMAKE_CODESIGN_val\n   ERROR_QUIET\n   OUTPUT_STRIP_TRAILING_WHITESPACE)\n  SET(CMAKE_CODESIGN ${CMAKE_CODESIGN_val} CACHE FILEPATH \"Codesign\")\n  message(STATUS \"Using codesign ${CMAKE_CODESIGN}\")\nENDIF()\n\nIF(NOT CMAKE_CODESIGN_ALLOCATE)\n  execute_process(\n    COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find codesign_allocate\n    OUTPUT_VARIABLE CMAKE_CODESIGN_ALLOCATE_val\n    ERROR_QUIET\n    OUTPUT_STRIP_TRAILING_WHITESPACE)\n  SET(CMAKE_CODESIGN_ALLOCATE ${CMAKE_CODESIGN_ALLOCATE_val} CACHE\n      FILEPATH \"Codesign_Allocate\")\n  message(STATUS \"Using codesign_allocate ${CMAKE_CODESIGN_ALLOCATE}\")\nENDIF()\n"
  },
  {
    "path": "cmake/dummy.c",
    "content": "int dummy() { return 42; }\n"
  },
  {
    "path": "cmake/llvm/QBDI_llvm.cmake",
    "content": "if(__add_qbdi_llvm)\n  return()\nendif()\nset(__add_qbdi_llvm ON)\n\ninclude(FetchContent)\n\n# configure FetchContent\nset(QBDI_LLVM_MAJOR_VERSION 19)\nset(QBDI_LLVM_VERSION 19.1.5)\n\n# download and include llvm cmake module\noption(QBDI_INCLUDE_LLVM_CMAKE_MODUKE \"Include llvm cmake module\" ON)\nif(QBDI_INCLUDE_LLVM_CMAKE_MODUKE)\n  FetchContent_Populate(\n    llvm_cmake\n    URL \"https://github.com/llvm/llvm-project/releases/download/llvmorg-${QBDI_LLVM_VERSION}/cmake-${QBDI_LLVM_VERSION}.src.tar.xz\"\n    URL_HASH\n      \"SHA256=a08ae477571fd5e929c27d3d0d28c6168d58dd00b6354c2de3266ae0d86ad44f\"\n    DOWNLOAD_DIR \"${QBDI_THIRD_PARTY_DIRECTORY}/llvm-cmake-download\"\n    SOURCE_DIR \"${FETCHCONTENT_BASE_DIR}/qbdi_llvm/cmake\"\n    BINARY_DIR \"${FETCHCONTENT_BASE_DIR}/llvm_cmake-build\"\n    SUBBUILD_DIR \"${FETCHCONTENT_BASE_DIR}/llvm_cmake-subbuild\"\n    QUIET)\nendif()\n\n# Variable also use in QBDI_llvm_tblgen\nset(QBDI_LLVM_URL\n    \"https://github.com/llvm/llvm-project/releases/download/llvmorg-${QBDI_LLVM_VERSION}/llvm-${QBDI_LLVM_VERSION}.src.tar.xz\"\n)\nset(QBDI_LLVM_URL_HASH\n    \"SHA256=7d71635948e4da1814ce8e15ec45399e4094a5442e86d352c96ded0f2b3171b6\")\n\nFetchContent_Populate(\n  llvm\n  URL \"${QBDI_LLVM_URL}\"\n  URL_HASH \"${QBDI_LLVM_URL_HASH}\"\n  DOWNLOAD_DIR \"${QBDI_THIRD_PARTY_DIRECTORY}/llvm-download\"\n  SOURCE_DIR \"${FETCHCONTENT_BASE_DIR}/qbdi_llvm/llvm\"\n  BINARY_DIR \"${FETCHCONTENT_BASE_DIR}/llvm-build\"\n  SUBBUILD_DIR \"${FETCHCONTENT_BASE_DIR}/llvm-subbuild\"\n  QUIET)\n\nset(CMAKE_CXX_STANDARD\n    17\n    CACHE STRING \"USE CPP 17\")\nset(LLVM_BUILD_TOOLS\n    OFF\n    CACHE BOOL \"Disable LLVM_BUILD_TOOLS\")\nset(LLVM_BUILD_UTILS\n    OFF\n    CACHE BOOL \"Disable LLVM_BUILD_UTILS\")\nset(LLVM_BUILD_TESTS\n    OFF\n    CACHE BOOL \"Disable LLVM_BUILD_TESTS\")\nset(LLVM_BUILD_BENCHMARKS\n    OFF\n    CACHE BOOL \"Disable LLVM_BUILD_BENCHMARKS\")\nset(LLVM_BUILD_EXAMPLES\n    OFF\n    CACHE BOOL \"Disable LLVM_BUILD_EXAMPLES\")\nset(LLVM_INCLUDE_TOOLS\n    OFF\n    CACHE BOOL \"Disable LLVM_INCLUDE_TOOLS\")\nset(LLVM_INCLUDE_UTILS\n    OFF\n    CACHE BOOL \"Disable LLVM_INCLUDE_UTILS\")\nset(LLVM_INCLUDE_TESTS\n    OFF\n    CACHE BOOL \"Disable LLVM_INCLUDE_TESTS\")\nset(LLVM_INCLUDE_BENCHMARKS\n    OFF\n    CACHE BOOL \"Disable LLVM_INCLUDE_BENCHMARKS\")\nset(LLVM_INCLUDE_EXAMPLES\n    OFF\n    CACHE BOOL \"Disable LLVM_INCLUDE_EXAMPLES\")\nset(LLVM_ENABLE_TERMINFO\n    OFF\n    CACHE BOOL \"Disable LLVM_ENABLE_TERMINFO\")\nset(LLVM_ENABLE_BINDINGS\n    OFF\n    CACHE BOOL \"Disable LLVM_ENABLE_BINDINGS\")\nset(LLVM_ENABLE_RTTI\n    OFF\n    CACHE BOOL \"Disable LLVM_ENABLE_RTTI\")\nset(LLVM_APPEND_VC_REV\n    OFF\n    CACHE BOOL \"Disable LLVM_APPEND_VC_REV\")\nset(LLVM_ENABLE_Z3_SOLVER\n    OFF\n    CACHE BOOL \"Disable LLVM_ENABLE_Z3_SOLVER\")\nset(LLVM_ENABLE_ZLIB\n    OFF\n    CACHE BOOL \"Disable LLVM_ENABLE_ZLIB\")\nset(LLVM_ENABLE_ZSTD\n    OFF\n    CACHE BOOL \"Disable LLVM_ENABLE_ZSTD\")\nset(LLVM_TARGET_ARCH\n    ${QBDI_LLVM_ARCH}\n    CACHE STRING \"set LLVM_ARCH\")\nset(LLVM_TARGETS_TO_BUILD\n    ${QBDI_LLVM_ARCH}\n    CACHE STRING \"set LLVM_TARGETS_TO_BUILD\")\n\nset(QBDI_LLVM_TRIPLE \"\")\nif(QBDI_ARCH_ARM)\n  if(QBDI_PLATFORM_ANDROID)\n    set(QBDI_LLVM_TRIPLE armv7-linux-androideabi)\n  else()\n    set(QBDI_LLVM_TRIPLE armv7-linux-gnu)\n  endif()\nelseif(QBDI_ARCH_AARCH64)\n  if(QBDI_PLATFORM_ANDROID)\n    set(QBDI_LLVM_TRIPLE aarch64-linux-android)\n  elseif(QBDI_PLATFORM_IOS)\n    set(LLVM_ENABLE_LIBCXX\n        ON\n        CACHE INTERNAL \"set LLVM_ENABLE_LIBCXX\")\n    set(QBDI_LLVM_TRIPLE arm64-apple-darwin)\n  else()\n    set(QBDI_LLVM_TRIPLE aarch64-linux-gnu)\n  endif()\nelseif(QBDI_ARCH_X86)\n  set(LLVM_BUILD_32_BITS\n      ON\n      CACHE INTERNAL \"set LLVM_BUILD_32_BITS\")\n  if(QBDI_PLATFORM_MACOS)\n    set(LLVM_ENABLE_LIBCXX\n        ON\n        CACHE INTERNAL \"set LLVM_ENABLE_LIBCXX\")\n    set(QBDI_LLVM_TRIPLE i386-apple-darwin17.7.0)\n  elseif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n    set(QBDI_LLVM_TRIPLE i386-pc-linux)\n  endif()\nelseif(QBDI_ARCH_X86_64)\n  if(QBDI_PLATFORM_LINUX)\n    set(QBDI_LLVM_TRIPLE x86_64-pc-linux-gnu)\n  endif()\nelse()\n  message(FATAL_ERROR \"Unsupported LLVM Architecture.\")\nendif()\n\nif(QBDI_PLATFORM_WINDOWS)\n  add_compile_definitions(_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)\nendif()\n\nif(NOT (\"${QBDI_LLVM_TRIPLE}\" STREQUAL \"\"))\n  set(LLVM_HOST_TRIPLE\n      \"${QBDI_LLVM_TRIPLE}\"\n      CACHE STRING \"set LLVM_HOST_TRIPLE\")\nendif()\n\n# build llvm with visibility hidden\nif(DEFINED CMAKE_C_VISIBILITY_PRESET)\n  set(QBDI_CACHE_CMAKE_C_VISIBILITY_PRESET ${CMAKE_C_VISIBILITY_PRESET})\nendif()\nif(DEFINED CMAKE_CXX_VISIBILITY_PRESET)\n  set(QBDI_CACHE_CMAKE_CXX_VISIBILITY_PRESET ${CMAKE_CXX_VISIBILITY_PRESET})\nendif()\nset(CMAKE_C_VISIBILITY_PRESET\n    \"hidden\"\n    CACHE STRING \"set CMAKE_C_VISIBILITY_PRESET\" FORCE)\nset(CMAKE_CXX_VISIBILITY_PRESET\n    \"hidden\"\n    CACHE STRING \"set CMAKE_CXX_VISIBILITY_PRESET\" FORCE)\n\nif(NOT (\"${NATIVE_TABLEGEN_PATH}\" STREQUAL \"\"))\n  set(LLVM_TABLEGEN\n      \"${NATIVE_TABLEGEN_PATH}\"\n      CACHE INTERNAL \"force tablegen\")\nelseif(NOT (\"${QBDI_LLVM_TABLEN_TOOLSCHAIN}\" STREQUAL \"\"))\n  # create a second directory to build the native llvm-tblgen\n  # mostly use when crosscompile and need another compiler to create a native\n  # target\n  include(QBDI_llvm_tblgen)\n  set(LLVM_TABLEGEN\n      \"${QBDI_LLVM_NATIVE_TBLGEN}\"\n      CACHE INTERNAL \"force tablegen\")\nelse()\n  # check if llvm-tblgen-X is available\n  find_program(LLVM_TABLEN_BIN NAMES llvm-tblgen-${QBDI_LLVM_MAJOR_VERSION})\n  message(STATUS \"LLVM Table Gen found: ${LLVM_TABLEN_BIN}\")\n  if(${LLVM_TABLEN_BIN_FOUND})\n    set(LLVM_TABLEGEN\n        \"${LLVM_TABLEN_BIN}\"\n        CACHE STRING \"force tablegen\")\n  endif()\nendif()\n\nif(QBDI_PLATFORM_IOS AND \"${LLVM_TABLEGEN}\" STREQUAL \"\")\n  # llvm fail to compile in this platform. Force to compile tablegen\n  # with native toolchains\n  set(QBDI_LLVM_TABLEN_TOOLSCHAIN\n      \"${CMAKE_CURRENT_BINARY_DIR}/QBDI_empty_toolchains.cmake\")\n  file(TOUCH \"${QBDI_LLVM_TABLEN_TOOLSCHAIN}\")\n  include(QBDI_llvm_tblgen)\n  set(LLVM_TABLEGEN\n      \"${QBDI_LLVM_NATIVE_TBLGEN}\"\n      CACHE INTERNAL \"force tablegen\")\nendif()\n\nif(QBDI_CCACHE AND CCACHE_FOUND)\n  set(LLVM_CCACHE_BUILD\n      ON\n      CACHE BOOL \"Enable CCACHE in llvm\")\nelse()\n  set(LLVM_CCACHE_BUILD\n      OFF\n      CACHE BOOL \"Enable CCACHE in llvm\")\nendif()\n\nif(QBDI_ASAN AND HAVE_FLAG_SANITIZE_ADDRESS)\n  set(LLVM_USE_SANITIZER\n      Address\n      CACHE STRING \"Enable ASAN\")\nendif()\n\nadd_subdirectory(${llvm_SOURCE_DIR} ${llvm_BINARY_DIR} EXCLUDE_FROM_ALL)\n\n# restore visibility\nif(DEFINED QBDI_CACHE_CMAKE_C_VISIBILITY_PRESET)\n  set(CMAKE_C_VISIBILITY_PRESET\n      ${QBDI_CACHE_CMAKE_C_VISIBILITY_PRESET}\n      CACHE INTERNAL \"set CMAKE_C_VISIBILITY_PRESET\" FORCE)\nelse()\n  unset(CMAKE_C_VISIBILITY_PRESET CACHE)\nendif()\nif(DEFINED QBDI_CACHE_CMAKE_CXX_VISIBILITY_PRESET)\n  set(CMAKE_CXX_VISIBILITY_PRESET\n      ${QBDI_CACHE_CMAKE_CXX_VISIBILITY_PRESET}\n      CACHE INTERNAL \"set CMAKE_CXX_VISIBILITY_PRESET\" FORCE)\nelse()\n  unset(CMAKE_CXX_VISIBILITY_PRESET CACHE)\nendif()\n\n# list of LLVM library to build\nset(QBDI_LLVM_TARGET_LIBRARY)\nset(QBDI_LLVM_LINK_LIBRARY)\nmacro(add_llvm_lib)\n  foreach(LIB ${ARGV})\n    if(\"${LIB}\" MATCHES \"^::@\")\n      continue()\n    endif()\n    string(FIND \"${LIB}\" \"::@\" pos)\n    if(\"${pos}\" EQUAL -1)\n      set(TARGETLIB \"${LIB}\")\n    else()\n      string(SUBSTRING \"${LIB}\" 0 \"${pos}\" TARGETLIB)\n    endif()\n    if((TARGET ${TARGETLIB}) AND NOT (${TARGETLIB} IN_LIST\n                                      QBDI_LLVM_TARGET_LIBRARY))\n      list(APPEND QBDI_LLVM_TARGET_LIBRARY ${TARGETLIB})\n      get_target_property(_LIB_LINK ${TARGETLIB} INTERFACE_LINK_LIBRARIES)\n      if(_LIB_LINK)\n        add_llvm_lib(${_LIB_LINK})\n      endif()\n    elseif(NOT (TARGET ${TARGETLIB}) AND NOT (${TARGETLIB} IN_LIST\n                                              QBDI_LLVM_LINK_LIBRARY))\n      list(APPEND QBDI_LLVM_LINK_LIBRARY ${TARGETLIB})\n    endif()\n  endforeach()\nendmacro()\n\nadd_llvm_lib(\n  LLVMBinaryFormat\n  LLVMMCDisassembler\n  LLVMMCParser\n  LLVMMC\n  LLVMSupport\n  LLVMObject\n  LLVMTextAPI\n  LLVMCore\n  LLVMBitReader\n  LLVMBitstreamReader\n  LLVMRemarks)\n\nif(QBDI_PLATFORM_MACOS OR QBDI_PLATFORM_IOS)\n  add_llvm_lib(LLVMDemangle)\nendif()\n\nif(QBDI_ARCH_ARM)\n  add_llvm_lib(LLVMARMAsmParser LLVMARMDisassembler LLVMARMDesc LLVMARMInfo\n               LLVMARMUtils)\nelseif(QBDI_ARCH_AARCH64)\n  add_llvm_lib(LLVMAArch64AsmParser LLVMAArch64Desc LLVMAArch64Disassembler\n               LLVMAArch64Info LLVMAArch64Utils)\nelseif(QBDI_ARCH_X86_64 OR QBDI_ARCH_X86)\n  add_llvm_lib(LLVMX86AsmParser LLVMX86Disassembler LLVMX86Desc LLVMX86Info)\nelse()\n  message(FATAL_ERROR \"Unsupported LLVM Architecture.\")\nendif()\n\nif(QBDI_PLATFORM_MACOS)\n  find_package(Python3 REQUIRED COMPONENTS Interpreter)\n\n  set(LLVMSupportFixName \"${llvm_BINARY_DIR}/libLLVMSupportFix.a\")\n  add_custom_command(\n    OUTPUT \"${LLVMSupportFixName}\"\n    COMMAND\n      \"${Python3_EXECUTABLE}\"\n      \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/llvm/rename_object.py\" -i\n      $<TARGET_FILE:LLVMSupport> -o \"${LLVMSupportFixName}\" -r Memory.cpp.o 1 -r\n      Error.cpp.o 1\n    COMMENT \"Fix LLVMSupport library\"\n    DEPENDS LLVMSupport\n    VERBATIM)\n  list(REMOVE_ITEM QBDI_LLVM_TARGET_LIBRARY LLVMSupport)\n  list(APPEND QBDI_LLVM_TARGET_LIBRARY \"${LLVMSupportFixName}\")\n\n  set(LLVMObjectFixName \"${llvm_BINARY_DIR}/libLLVMObjectFix.a\")\n  add_custom_command(\n    OUTPUT \"${LLVMObjectFixName}\"\n    COMMAND\n      \"${Python3_EXECUTABLE}\"\n      \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/llvm/rename_object.py\" -i\n      $<TARGET_FILE:LLVMObject> -o \"${LLVMObjectFixName}\" -r Minidump.cpp.o 1\n    COMMENT \"Fix LLVMObject library\"\n    DEPENDS LLVMObject\n    VERBATIM)\n  list(REMOVE_ITEM QBDI_LLVM_TARGET_LIBRARY LLVMObject)\n  list(APPEND QBDI_LLVM_TARGET_LIBRARY \"${LLVMObjectFixName}\")\n\n  list(APPEND QBDI_LLVM_LINK_LIBRARY -lc++)\nelseif(QBDI_PLATFORM_LINUX)\n  list(APPEND QBDI_LLVM_LINK_LIBRARY -lstdc++)\nendif()\n\nmerge_static_libs(qbdi-llvm qbdi-llvm \\${QBDI_LLVM_TARGET_LIBRARY})\ntarget_link_libraries(qbdi-llvm INTERFACE ${QBDI_LLVM_LINK_LIBRARY})\n\ntarget_include_directories(\n  qbdi-llvm\n  INTERFACE ${llvm_SOURCE_DIR}/include\n  INTERFACE ${llvm_BINARY_DIR}/include\n  INTERFACE ${llvm_SOURCE_DIR}/lib/Target/${QBDI_LLVM_ARCH}\n  INTERFACE ${llvm_BINARY_DIR}/lib/Target/${QBDI_LLVM_ARCH}\n  INTERFACE ${llvm_SOURCE_DIR}/lib\n  INTERFACE ${llvm_BINARY_DIR}/lib)\n\nadd_custom_target(llvm DEPENDS qbdi-llvm)\n"
  },
  {
    "path": "cmake/llvm/QBDI_llvm_tblgen.cmake",
    "content": "if(__add_qbdi_llvm_tblgen)\n  return()\nendif()\nset(__add_qbdi_llvm_tblgen ON)\ninclude(ExternalProject)\n\nset(NATIVE_BUILD_DIR \"${CMAKE_CURRENT_BINARY_DIR}/QBDI_llvm_tblgen_native\")\n\nif(CMAKE_CONFIGURATION_TYPES)\n  set(QBDI_LLVM_NATIVE_TBLGEN \"${NATIVE_BUILD_DIR}/Release/bin/llvm-tblgen\")\nelse()\n  set(QBDI_LLVM_NATIVE_TBLGEN \"${NATIVE_BUILD_DIR}/bin/llvm-tblgen\")\nendif()\n\nExternalProject_Add(\n  llvm_tblgen\n  URL \"${QBDI_LLVM_URL}\"\n  URL_HASH \"${QBDI_LLVM_URL_HASH}\"\n  DOWNLOAD_DIR \"${QBDI_THIRD_PARTY_DIRECTORY}/llvm-download\"\n  SOURCE_DIR \"${llvm_cmake_SOURCE_DIR}/../llvm_tblgen_source_dir\"\n  BINARY_DIR \"${NATIVE_BUILD_DIR}\"\n  CONFIGURE_COMMAND\n    \"${CMAKE_COMMAND}\" -G \"${CMAKE_GENERATOR}\" -S <SOURCE_DIR> -B <BINARY_DIR>\n    \"-DCMAKE_MAKE_PROGRAM='${CMAKE_MAKE_PROGRAM}'\"\n    \"-DCMAKE_TOOLCHAIN_FILE='${QBDI_LLVM_TABLEN_TOOLSCHAIN}'\"\n    -DLLVM_TARGET_IS_CROSSCOMPILE_HOST=TRUE\n    \"-DLLVM_TARGETS_TO_BUILD='${LLVM_TARGETS_TO_BUILD}'\"\n    \"-DLLVM_TARGET_ARCH='${LLVM_TARGET_ARCH}'\" -DCMAKE_BUILD_TYPE=Release\n    \"-DLLVM_CCACHE_BUILD='${LLVM_CCACHE_BUILD}'\" -DLLVM_INCLUDE_TESTS=OFF\n    -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF\n  BUILD_COMMAND \"${CMAKE_COMMAND}\" --build <BINARY_DIR> --target llvm-tblgen\n  BUILD_BYPRODUCTS \"${QBDI_LLVM_NATIVE_TBLGEN}\"\n  INSTALL_COMMAND \"\")\n"
  },
  {
    "path": "cmake/llvm/rename_object.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nimport subprocess\nimport tempfile\nimport os\nimport sys\n\nLIPO = \"lipo\"\nIOS_LIPO=\"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/lipo\"\n\nif os.path.exists(IOS_LIPO):\n    LIPO = IOS_LIPO\n\ndef eprint(*args, **kwargs):\n    print(*args, file=sys.stderr, **kwargs)\n\ndef listArchs(archive):\n    try:\n        out = subprocess.check_output([LIPO, \"-info\", archive]).decode(\"utf-8\")\n    except:\n        out = \"\"\n\n    xs = out.split(\":\")\n\n    if (len(xs)) != 3:\n        raise RuntimeError(\"invalid lipo -info output\")\n\n    return xs[2].strip().split()\n\n\ndef extractArch(archive, arch, path):\n    name = arch + \"-\" + os.path.basename(archive)\n    fpath = os.path.join(path, name)\n    try:\n        t = subprocess.check_call([LIPO, \"-thin\", arch, archive, \"-output\", fpath])\n    except:\n        t = 1\n    if t != 0:\n        return \"\"\n    return fpath\n\n\ndef extractObjs(archive, path):\n    try:\n        t = subprocess.check_call([\"ar\", \"-x\", archive], cwd=path)\n    except:\n        t = 1\n    if t != 0:\n        return False\n    return True\n\n\ndef archiveObjs(archive, path):\n    vext = lambda x: x[-2:] == \".o\" or x[-3:] == \".o2\"\n    files = filter(vext, os.listdir(path))\n    try:\n        t = subprocess.check_call([\"libtool\", \"-static\", \"-o\", archive] + files, cwd=path)\n    except:\n        t = 1\n    if t != 0:\n        return False\n    return True\n\n\ndef mergeArch(archives, outpath):\n    if not archives:\n        return\n    try:\n        t = subprocess.check_call([LIPO, \"-create\"] + archives + [\"-output\", outpath])\n    except:\n        t = 1\n    if t != 0:\n        return False\n    return True\n\n\ndef rename_object(archive, ofname, suffix=\"\", remove=False):\n    root, ext = os.path.splitext(ofname)\n    nfname = \"{}_{}{}\".format(root, suffix, ext)\n    altofname = \"{}.o2\".format(root)\n    altnfname = \"{}_{}.o2\".format(root, suffix)\n\n    archs = listArchs(archive)\n    isFat = len(archs) > 1\n\n    # Create tmp dir\n    with tempfile.TemporaryDirectory() as tdir:\n        marchives = []\n\n        for arch in archs:\n            # Extract arch from universal binary\n            thinar = archive\n            if isFat:\n                thinar = extractArch(archive, arch, tdir)\n                if not thinar:\n                    eprint(\"Cannot extract arch \" + arch)\n                    continue\n            # Create tmp dir for extracted objects\n            arname = os.path.basename(thinar)\n            xdir = os.path.join(tdir, arname) + \".dir\"\n            os.mkdir(xdir)\n            # Extract objects\n            if not extractObjs(thinar, xdir):\n                eprint(\"Cannot extract objects for \" + arch)\n                continue\n            ofpath = os.path.join(xdir, ofname)\n            nfpath = os.path.join(xdir, nfname)\n            altofpath = os.path.join(xdir, altofname)\n            altnfpath = os.path.join(xdir, altnfname)\n            if remove:\n                os.unlink(ofpath)\n            else:\n                # Rename object\n                rename = False\n                if os.path.exists(ofpath):\n                    print(\"Move {} to {}\".format(ofpath, nfpath))\n                    os.rename(ofpath, nfpath)\n                    rename = True\n                if os.path.exists(altofpath):\n                    print(\"Move {} to {}\".format(altofpath, altnfpath))\n                    os.rename(altofpath, altnfpath)\n                    rename = True\n                if not rename:\n                    eprint(\"Cannot find object {} for arch {}\".format(ofname, arch))\n                    continue\n            # Archive objects\n            archiveObjs(thinar, xdir)\n            # Add archive to modified archive list\n            marchives.append(thinar)\n\n        if isFat:\n            # Merge all subarch into original file\n            if not mergeArch(marchives, archive):\n                raise RuntimeError(\"Cannot update original file\")\n\nif __name__ == '__main__':\n    import argparse\n    import shutil\n    parser = argparse.ArgumentParser()\n\n    parser.add_argument(\"-i\", \"--input\", type=str, help=\"input archive\", required=True)\n    parser.add_argument(\"-o\", \"--output\", type=str, help=\"output archive\", required=True)\n    parser.add_argument(\"-r\", \"--rename-object\", type=str, help=\"Object to rename\", action='append', default=[],\n            metavar=('name','suffix'), nargs=2)\n    parser.add_argument(\"-R\", \"--remove-object\", type=str, help=\"Object to remove\", action='append', default=[])\n\n    args = parser.parse_args()\n\n    assert os.path.exists(args.input), \"Input file not found ({})\".format(args.input)\n    if args.input != args.output:\n        os.makedirs(os.path.split(os.path.realpath(args.input))[0], exist_ok=True)\n        shutil.copy2(args.input, args.output)\n        removeOnFail = True\n    else:\n        removeOnFail = False\n\n    try:\n        for oname, suffix in args.rename_object:\n            rename_object(args.output, oname, suffix=suffix)\n        for oname in args.remove_object:\n            rename_object(args.output, oname, remove=True)\n        removeOnFail = False\n    finally:\n        if removeOnFail:\n            os.unlink(args.output)\n\n"
  },
  {
    "path": "cmake/merge_archives.cmake",
    "content": "set(MERGE_LIBS_DUMMY_FILE ${CMAKE_CURRENT_LIST_DIR}/dummy.c)\n\nmacro(MERGE_STATIC_LIBS TARGET OUTPUT_NAME LIBS_TO_MERGE)\n\n  set(SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_depends.c)\n\n  add_custom_command(\n    OUTPUT ${SOURCE_FILE}\n    COMMAND ${CMAKE_COMMAND} -E copy ${MERGE_LIBS_DUMMY_FILE} ${SOURCE_FILE}\n    DEPENDS ${LIBS_TO_MERGE})\n\n  add_library(${TARGET} STATIC ${SOURCE_FILE})\n  set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME ${OUTPUT_NAME})\n\n  set(LIBS_PATH \"\")\n  foreach(LIB ${LIBS_TO_MERGE})\n    if(TARGET ${LIB})\n      add_dependencies(${TARGET} ${LIB})\n      set(LIBS_PATH ${LIBS_PATH} $<TARGET_FILE:${LIB}>)\n    else()\n      set(LIBS_PATH ${LIBS_PATH} ${LIB})\n    endif()\n  endforeach()\n  list(REMOVE_DUPLICATES LIBS_PATH)\n\n  if(WIN32)\n\n    set(LIB_COMMAND_ARGS_FILE\n        \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_lib_exe_args\")\n    string(JOIN \" \" LINK_LIBS ${LIBS_PATH})\n    file(\n      GENERATE\n      OUTPUT ${LIB_COMMAND_ARGS_FILE}\n      CONTENT \"/OUT:$<TARGET_FILE:${TARGET}> ${LINK_LIBS}\")\n\n    add_custom_command(\n      TARGET ${TARGET}\n      POST_BUILD\n      COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${TARGET}>\n      COMMAND \"LIB.EXE\" \"@${LIB_COMMAND_ARGS_FILE}\")\n\n  elseif(APPLE)\n    add_custom_command(\n      TARGET ${TARGET}\n      POST_BUILD\n      COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${TARGET}>\n      COMMAND /usr/bin/libtool -static -o $<TARGET_FILE:${TARGET}> ${LIBS_PATH})\n\n  elseif(UNIX)\n    set(AR_COMMAND_ARGS_FILE\n        \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_ar_script_file\")\n\n    set(AR_SCRIPT \"\")\n    foreach(LIB ${LIBS_PATH})\n      set(AR_SCRIPT \"${AR_SCRIPT}\\naddlib ${LIB}\")\n    endforeach()\n\n    file(\n      GENERATE\n      OUTPUT ${AR_COMMAND_ARGS_FILE}\n      CONTENT \"create $<TARGET_FILE:${TARGET}>\\n${AR_SCRIPT}\\nsave\\nend\\n\")\n\n    add_custom_command(\n      TARGET ${TARGET}\n      POST_BUILD\n      COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${TARGET}>\n      COMMAND ${CMAKE_AR} \"-M\" \"<${AR_COMMAND_ARGS_FILE}\")\n\n  else()\n    message(\n      FATAL_ERROR \"Static llvm library: unsupported system ${CMAKE_SYSTEM_NAME}\"\n    )\n  endif()\n\nendmacro()\n"
  },
  {
    "path": "docker/.gitignore",
    "content": "image.hash\n"
  },
  {
    "path": "docker/archlinux/Dockerfile.X86",
    "content": "FROM archlinux:base-devel\nLABEL maintainer=\"QBDI Team <qbdi@quarkslab.com>\"\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    QBDI_PLATFORM=\"linux\" \\\n    QBDI_ARCH=\"X86\"\n\n# Get latest package list, upgrade packages, install required packages \n# and cleanup to keep container as small as possible\nRUN echo \"[multilib]\" >> /etc/pacman.conf && \\\n    echo \"Include = /etc/pacman.d/mirrorlist\" >> /etc/pacman.conf && \\\n    pacman -Suy --noconfirm && \\\n    pacman -S --noconfirm \\\n        base-devel \\\n        sudo \\\n        gcc \\\n        git \\\n        ninja \\\n        cmake \\\n        python3 \\\n        wget \\\n        multilib-devel && \\\n    (pacman -Sc --noconfirm || true)\n\n# create a user\nRUN useradd -Groot $USER && \\\n    echo '%root ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers\n\n# switch to new user\nUSER $USER\n\n# build / test / install QBDI\n\n# git archive -o qbdi.tar.gz --prefix=qbdi/ HEAD .\nADD qbdi.tar.gz $HOME/\n\nWORKDIR $HOME/qbdi\n\nRUN sudo chown -R $USER:$USER . && \\\n    rm -rf deps && \\\n    ln -s $HOME/qbdi-deps/deps deps && \\\n    mkdir build && \\\n    cd build && \\\n    cp ../docker/archlinux/PKGBUILD.$QBDI_ARCH PKGBUILD && \\\n    makepkg -fc && \\\n    sudo pacman -U --noconfirm QBDI-*.pkg.tar.zst\n\nWORKDIR \"$HOME/\"\nCMD [\"/bin/bash\"]\n"
  },
  {
    "path": "docker/archlinux/Dockerfile.X86_64",
    "content": "FROM archlinux:base-devel\nLABEL maintainer=\"QBDI Team <qbdi@quarkslab.com>\"\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    QBDI_PLATFORM=\"linux\" \\\n    QBDI_ARCH=\"X86_64\"\n\n# Get latest package list, upgrade packages, install required packages \n# and cleanup to keep container as small as possible\nRUN pacman -Suy --noconfirm && \\\n    pacman -S --noconfirm \\\n        base-devel \\\n        sudo \\\n        gcc \\\n        git \\\n        ninja \\\n        cmake \\\n        python3 \\\n        wget && \\\n    (pacman -Sc --noconfirm || true)\n\n# create a user\nRUN useradd -Groot $USER && \\\n    echo '%root ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers\n\n# switch to new user\nUSER $USER\n\n# build / test / install QBDI\n\n# git archive -o qbdi.tar.gz --prefix=qbdi/ HEAD .\nADD qbdi.tar.gz $HOME/\n\nWORKDIR $HOME/qbdi\n\nRUN sudo chown -R $USER:$USER . && \\\n    rm -rf deps && \\\n    ln -s $HOME/qbdi-deps/deps deps && \\\n    mkdir build && \\\n    cd build && \\\n    cp ../docker/archlinux/PKGBUILD.$QBDI_ARCH PKGBUILD && \\\n    makepkg -fc && \\\n    sudo pacman -U --noconfirm QBDI-*.pkg.tar.zst\n\nWORKDIR \"$HOME/\"\nCMD [\"/bin/bash\"]\n"
  },
  {
    "path": "docker/archlinux/PKGBUILD.X86",
    "content": "# Maintainer: Charles Hubain <chubain@quarkslab.com>\n# Maintainer: QBDI Team <qbdi@quarkslab.com>\n\npkgname=QBDI-X86\npkgver=0.12.2_devel\npkgrel=1\nepoch=\npkgdesc=\"QuarkslaB Dynamic binary Instrumentation for linux-X86\"\narch=('x86_64' 'i686')\nurl=\"https://qbdi.quarkslab.com/\"\nlicense=('apache2')\noptions=(staticlibs)\nmakedepends=('cmake' 'ninja' 'gcc' 'python' 'git')\n\nprepare() {\n    mkdir \"$pkgname-$pkgver\"\n}\n\nbuild() {\n    cd \"$pkgname-$pkgver\"\n    cmake -DCMAKE_BUILD_TYPE=Release \\\n          -DCMAKE_CROSSCOMPILING=FALSE \\\n          -DQBDI_PLATFORM=linux \\\n          -DQBDI_ARCH=X86 \\\n          -DCMAKE_INSTALL_PREFIX=/usr \\\n          -DQBDI_TOOLS_PYQBDI=OFF \\\n          -DQBDI_CCACHE=OFF \\\n          -G Ninja \\\n          ../../../\n    cmake --build .\n}\n\ncheck() {\n    cd \"$pkgname-$pkgver\"\n    ./test/QBDITest\n}\n\npackage() {\n    cd \"$pkgname-$pkgver\"\n    DESTDIR=\"$pkgdir/\" cmake --build . --target install\n}\n"
  },
  {
    "path": "docker/archlinux/PKGBUILD.X86_64",
    "content": "# Maintainer: Charles Hubain <chubain@quarkslab.com>\n# Maintainer: QBDI Team <qbdi@quarkslab.com>\n\npkgname=QBDI-X86_64\npkgver=0.12.2_devel\npkgrel=1\nepoch=\npkgdesc=\"QuarkslaB Dynamic binary Instrumentation for linux-X86_64\"\narch=('x86_64')\nurl=\"https://qbdi.quarkslab.com/\"\nlicense=('apache2')\noptions=(staticlibs)\nmakedepends=('cmake' 'ninja' 'gcc' 'python' 'git')\n\nprepare() {\n    mkdir \"$pkgname-$pkgver\"\n}\n\nbuild() {\n    cd \"$pkgname-$pkgver\"\n    cmake -DCMAKE_BUILD_TYPE=Release \\\n          -DCMAKE_CROSSCOMPILING=FALSE \\\n          -DQBDI_PLATFORM=linux \\\n          -DQBDI_ARCH=X86_64 \\\n          -DCMAKE_INSTALL_PREFIX=/usr \\\n          -DQBDI_TOOLS_PYQBDI=OFF \\\n          -DQBDI_CCACHE=OFF \\\n          -G Ninja \\\n          ../../../\n    cmake --build .\n}\n\ncheck() {\n    cd \"$pkgname-$pkgver\"\n    ./test/QBDITest\n}\n\npackage() {\n    cd \"$pkgname-$pkgver\"\n    DESTDIR=\"$pkgdir/\" cmake --build . --target install\n}\n"
  },
  {
    "path": "docker/archlinux/build.sh",
    "content": "#!/usr/bin/bash\n\nset -e\nset -x\n\nBASEDIR=$(cd $(dirname \"$0\") && pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\n. \"${BASEDIR}/../common.sh\"\n\nARCH=\"X86_64\"\n\nif [[ \"$1\" = \"X86\" || \"$1\" = \"x86\" ]]; then\n    ARCH=\"X86\"\nfi\n\nDOCKER_TAG=\"qbdi:x${ARCH: -2}_archlinux\"\n\nprepare_archive\n\ndocker build \"${BASEDIR}\" -t \"${DOCKER_TAG}\" -f \"${BASEDIR}/Dockerfile.${ARCH}\"\n\ndelete_archive\n\necho \"Success build ${DOCKER_TAG}\"\n"
  },
  {
    "path": "docker/ci_linux/Dockerfile",
    "content": "ARG DOCKER_IMG=\"debian:13\"\n\nFROM $DOCKER_IMG\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    CLICOLOR_FORCE=1\n\nARG USER_ID=1000\n\n# Get latest package list, upgrade packages, install required packages \n# and cleanup to keep container as small as possible\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n        adduser \\\n        bash \\\n        git \\\n        build-essential \\\n        ccache \\\n        cmake \\\n        g++ \\\n        g++-multilib \\\n        libstdc++-13-dev \\\n        make \\\n        ninja-build \\\n        pkg-config \\\n        wget \\\n        ca-certificates \\\n        python3 \\\n        python3-dev \\\n        python3-yaml \\\n        zip \\\n        git \\\n        xxd && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n# create a user\nRUN adduser --uid \"$USER_ID\" --disabled-password --gecos '' --home \"$HOME\" \"$USER\"\n\nWORKDIR $HOME\n\nUSER $USER\n\n"
  },
  {
    "path": "docker/ci_linux/build-qbdi.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd \"${HOME}/qbdi/\"\nmkdir -p build\ncd build\n\ncmake .. \\\n      -G Ninja \\\n      -DCMAKE_BUILD_TYPE=Release \\\n      -DCMAKE_CROSSCOMPILING=FALSE \\\n      -DQBDI_PLATFORM=${QBDI_PLATFORM} \\\n      -DQBDI_ARCH=${QBDI_ARCH} \\\n      -DCMAKE_INSTALL_PREFIX=$PREFIX \\\n      -DQBDI_EXAMPLES=ON \\\n      -DQBDI_TOOLS_VALIDATOR=ON \\\n      -DQBDI_TOOLS_PYQBDI=ON\n\nninja\n\ncpack\n\n./test/QBDITest\n\nset +e\n\ncd \"${HOME}/qbdi/\"\nmkdir -p tools/validation_runner/travis_db\n\npython3 tools/validation_runner/ValidationRunner.py tools/validation_runner/travis.cfg\n\nexit 0\n"
  },
  {
    "path": "docker/ci_linux/img_build.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd $(dirname \"$0\")\nBASEDIR=$(pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\nif [[ \"${QBDI_ARCH}\" = \"X86_64\" ]]; then\n    DOCKER_IMG=\"amd64/debian:13\"\nelif [[ \"${QBDI_ARCH}\" = \"X86\" ]]; then\n    DOCKER_IMG=\"i386/debian:13\"\nelse\n    echo \"Unknown QBDI_ARCH : ${QBDI_ARCH}\"\n    exit 1\nfi\n\ndocker build \"${BASEDIR}\" -t qbdi_build:base_${QBDI_ARCH} --build-arg USER_ID=\"$(id -u)\" --build-arg DOCKER_IMG=\"${DOCKER_IMG}\" -f \"${BASEDIR}/Dockerfile\"\n"
  },
  {
    "path": "docker/ci_linux/qbdi.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd $(dirname \"$0\")\nBASEDIR=$(pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\n./img_build.sh\n\ndocker run --rm \\\n    -e QBDI_PLATFORM=\"${QBDI_PLATFORM}\" \\\n    -e QBDI_ARCH=\"${QBDI_ARCH}\" \\\n    --mount type=bind,source=\"${GITDIR}\",target=/home/docker/qbdi \\\n    --mount type=bind,source=\"${HOME}/.ccache\",target=/home/docker/.ccache \\\n    --cap-add=SYS_PTRACE --security-opt seccomp:unconfined \\\n    qbdi_build:base_${QBDI_ARCH} \\\n    /bin/bash /home/docker/qbdi/docker/ci_linux/build-qbdi.sh\n\n\n"
  },
  {
    "path": "docker/ci_linux_arm/.gitignore",
    "content": ".dockcross-linux-AARCH64-latest\n.dockcross-linux-ARM-latest\n\n"
  },
  {
    "path": "docker/ci_linux_arm/build_qbdi_linux.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd \"$(dirname \"$0\")\"\nBASEDIR=\"$(pwd -P)\"\nGITDIR=\"$(git rev-parse --show-toplevel)\"\n\nTAG_PREFIX=\"qbdi-linux/qbdi_test\"\nQBDI_PLATFORM=\"linux\"\nQBDI_ARCH=\"$1\"\n\n./images/img_build.sh \"${QBDI_ARCH}\"\n\nIMG_BUILD=\"${TAG_PREFIX}:dockcross_${QBDI_PLATFORM}_${QBDI_ARCH}\"\n\n#1 get dockcross script\nSCRIPT_PATH=\"${BASEDIR}/.dockcross-${QBDI_PLATFORM}-${QBDI_ARCH}-latest\"\ndocker run --rm \"${IMG_BUILD}\" > \"${SCRIPT_PATH}\"\nchmod +x \"${SCRIPT_PATH}\"\n\n#2 Don't mount .ssh in docker\nexport SSH_DIR=/var/empty/.ssh\n\n#3 use docker to run the package\nexport OCI_EXE=docker\n\n#4 compile and test QBDI in dockcross\npushd \"${GITDIR}\"\n\"${SCRIPT_PATH}\" \\\n    -i \"${IMG_BUILD}\" \\\n    -a \"-v ${HOME}/.ccache:${HOME}/.ccache\" \\\n    -- env QBDI_PLATFORM=\"${QBDI_PLATFORM}\" \\\n           QBDI_ARCH=\"${QBDI_ARCH}\" \\\n           ./docker/ci_linux_arm/docker_internal_script/build-test-linux.sh\npopd\n\n"
  },
  {
    "path": "docker/ci_linux_arm/docker_internal_script/build-test-linux.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\nmkdir -p \"build-docker-${QBDI_PLATFORM}-${QBDI_ARCH}\"\ncd \"build-docker-${QBDI_PLATFORM}-${QBDI_ARCH}\"\n\n# generate clean toolchains file\nTOOLCHAIN_FILE=\"QBDI_${QBDI_ARCH}_Toolchain.cmake\"\ncat \"${CMAKE_TOOLCHAIN_FILE}\" > \"${TOOLCHAIN_FILE}\"\nsed -e \"s#\\$ENV{CROSS_TRIPLE}#${CROSS_TRIPLE}#\" \\\n    -e \"s#\\$ENV{CROSS_ROOT}#${CROSS_ROOT}#\" \\\n    -e \"s#\\$ENV{CC}#${CC}#\" \\\n    -e \"s#\\$ENV{CXX}#${CXX}#\" \\\n    -e \"s#\\$ENV{FC}#${FC}#\" \\\n    -i \"${TOOLCHAIN_FILE}\"\n\n# create an empty toolchains to break the propagation of crosscompile\n# environment when compile llvm-tblgen\nEMPTY_TOOLCHAINS=\"$(pwd)/QBDI_empty_toolchains.cmake\"\ntouch \"${EMPTY_TOOLCHAINS}\"\n\n# need to have a clear env to avoid crosscompiler\n# to be used to compile llvm-tblgen\nclean_env() {\n  env -i \\\n    PATH=\"${PATH}\" \\\n    HOME=\"${HOME}\" \\\n    CLICOLOR_FORCE=\"1\" \\\n    \"$@\"\n}\n\nclean_env \\\n    cmake -G Ninja \\\n      -DCMAKE_TOOLCHAIN_FILE=\"${TOOLCHAIN_FILE}\" \\\n      -DCMAKE_BUILD_TYPE=Release \\\n      -DQBDI_LLVM_TABLEN_TOOLSCHAIN=\"${EMPTY_TOOLCHAINS}\" \\\n      -DQBDI_PLATFORM=\"${QBDI_PLATFORM}\" \\\n      -DQBDI_ARCH=\"${QBDI_ARCH}\" \\\n      -DQBDI_EXAMPLES=ON \\\n      -DQBDI_TOOLS_VALIDATOR=OFF \\\n      -DQBDI_TOOLS_PYQBDI=OFF \\\n      ..\n\nclean_env ninja\n\nif [[ \"${QBDI_ARCH}\" = \"ARM\" ]]; then\n  qemu-arm-static ./test/QBDITest\nelif [[ \"${QBDI_ARCH}\" = \"AARCH64\" ]]; then\n  qemu-aarch64-static ./test/QBDITest\nfi\n\nclean_env cpack\n\nexit 0\n"
  },
  {
    "path": "docker/ci_linux_arm/images/Dockerfile.dockcross",
    "content": "ARG DOCKER_IMG=\"dockcross/linux-arm64\"\n\nFROM $DOCKER_IMG\n\nENV CLICOLOR_FORCE=1\n\n# setup backport to use cmake >= 3.28\nRUN echo 'deb http://deb.debian.org/debian bookworm-backports main contrib non-free' > /etc/apt/sources.list.d/backports.list\n\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n        bash \\\n        ca-certificates \\\n        ccache \\\n        cmake/bookworm-backports \\\n        git \\\n        python3 \\\n        qemu-user-static \\\n        wget \\\n        zip && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n"
  },
  {
    "path": "docker/ci_linux_arm/images/img_build.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd \"$(dirname \"$0\")\"\nBASEDIR=\"$(pwd -P)\"\nTAG_PREFIX=\"qbdi-linux/qbdi_test\"\nTARGET_ARCH=\"$1\"\n\nif [[ -n \"$TARGET_ARCH\" ]] && [[ \"$TARGET_ARCH\" != \"AARCH64\" ]] && [[ \"$TARGET_ARCH\" != \"ARM\" ]]; then\n  echo \"Unrecognized architecture.\"\n  echo \"Supported : AARCH64, ARM\"\n  exit 1\nfi\n\nbuild_linux() {\n  QBDI_PLATFORM=\"linux\"\n  QBDI_ARCH=\"$1\"\n  if [[ \"$QBDI_ARCH\" = \"AARCH64\" ]]; then\n    DOCKCROSS_IMG=\"dockcross/linux-arm64\"\n  elif [[ \"$QBDI_ARCH\" = \"ARM\" ]]; then\n    DOCKCROSS_IMG=\"dockcross/linux-armv7\"\n  fi\n  docker pull \"${DOCKCROSS_IMG}\"\n  docker build \"${BASEDIR}\" -f \"${BASEDIR}/Dockerfile.dockcross\" -t \"${TAG_PREFIX}:dockcross_${QBDI_PLATFORM}_${QBDI_ARCH}\" --build-arg DOCKER_IMG=\"${DOCKCROSS_IMG}\"\n}\n\nif [[ -n \"$TARGET_ARCH\" ]]; then\n  build_linux \"$TARGET_ARCH\"\nelse\n  build_linux AARCH64\n  build_linux ARM\nfi\n\n"
  },
  {
    "path": "docker/ci_python_linux/Dockerfile",
    "content": "ARG DOCKER_IMG=\"quay.io/pypa/manylinux2014\"\n\nFROM $DOCKER_IMG\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    QBDI_PLATFORM=\"linux\" \\\n    PYTHON_OPT=\"/opt/python\" \\\n    CLICOLOR_FORCE=1 \\\n    CCACHE_VERSION=\"4.11.3\" \\\n    CCACHE_HASH=\"28a407314f03a7bd7a008038dbaffa83448bc670e2fc119609b1d99fb33bb600\"\n\nARG USER_ID=1000\n\n# create a user\nRUN adduser --uid \"$USER_ID\" --password '' -m \"$USER\"\n\n# Get latest package list, upgrade packages, install required packages\n# and cleanup to keep container as small as possible\nRUN yum update -y && \\\n    yum install -y \\\n        bash \\\n        wget \\\n        ca-certificates && \\\n    (yum install -y ccache || true) && \\\n    yum clean all\n\nRUN /opt/python/cp38-cp38/bin/pip install ninja==1.10.0.post2\n\nENV PATH=$PATH:/opt/python/cp38-cp38/bin/ \\\n    CCACHE_URL=\"https://github.com/ccache/ccache/releases/download/v${CCACHE_VERSION}/ccache-${CCACHE_VERSION}.tar.gz\" \\\n    CCACHE_ARCHIVE=\"ccache-${CCACHE_VERSION}.tar.gz\" \n\nRUN if ! which ccache >/dev/null 2>&1; then \\\n        set -ex; \\\n        cd /tmp; \\\n        wget \"${CCACHE_URL}\" -O \"${CCACHE_ARCHIVE}\"; \\\n        (echo \"${CCACHE_HASH} ${CCACHE_ARCHIVE}\" | sha256sum -c -) || exit 1; \\\n        tar xf \"${CCACHE_ARCHIVE}\"; \\\n        cd \"/tmp/ccache-${CCACHE_VERSION}\"; \\\n        mkdir build && cd build; \\\n        cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DZSTD_FROM_INTERNET=ON; \\\n        ninja install; \\\n        cd /; \\\n        rm -rf \"/tmp/ccache-${CCACHE_VERSION}\" \"/tmp/${CCACHE_ARCHIVE}\"; \\\n    fi\n\nWORKDIR $HOME\n\nUSER $USER\n\n"
  },
  {
    "path": "docker/ci_python_linux/build_whl.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd ~/qbdi\n\nPYTHON_VERSION=\"${1:-all}\"\n\nif [[ \"${PYTHON_VERSION}\" = \"all\" ]] || [[ \"${PYTHON_VERSION}\" = \"3.8\" ]]; then\n  /opt/python/cp38-cp38/bin/python -m build -w\nfi\nif [[ \"${PYTHON_VERSION}\" = \"all\" ]] || [[ \"${PYTHON_VERSION}\" = \"3.9\" ]]; then\n  /opt/python/cp39-cp39/bin/python -m build -w\nfi\nif [[ \"${PYTHON_VERSION}\" = \"all\" ]] || [[ \"${PYTHON_VERSION}\" = \"3.10\" ]]; then\n  /opt/python/cp310-cp310/bin/python -m build -w\nfi\nif [[ \"${PYTHON_VERSION}\" = \"all\" ]] || [[ \"${PYTHON_VERSION}\" = \"3.11\" ]]; then\n  /opt/python/cp311-cp311/bin/python -m build -w\nfi\nif [[ \"${PYTHON_VERSION}\" = \"all\" ]] || [[ \"${PYTHON_VERSION}\" = \"3.12\" ]]; then\n  /opt/python/cp312-cp312/bin/python -m build -w\nfi\nif [[ \"${PYTHON_VERSION}\" = \"all\" ]] || [[ \"${PYTHON_VERSION}\" = \"3.13\" ]]; then\n  /opt/python/cp313-cp313/bin/python -m build -w\nfi\nif [[ \"${PYTHON_VERSION}\" = \"all\" ]] || [[ \"${PYTHON_VERSION}\" = \"3.14\" ]]; then\n  /opt/python/cp314-cp314/bin/python -m build -w\nfi\n\nif [[ \"${QBDI_ARCH}\" = \"X86_64\" ]]; then\n    for i in dist/*_x86_64.whl; do\n        auditwheel repair $i -w outwheel;\n    done\nelif [[ \"${QBDI_ARCH}\" = \"X86\" ]]; then\n    for i in dist/*_i686.whl; do\n        auditwheel repair $i -w outwheel;\n    done\nelif [[ \"${QBDI_ARCH}\" = \"AARCH64\" ]]; then\n    for i in dist/*_aarch64.whl; do\n        auditwheel repair $i -w outwheel;\n    done\nelse\n    echo \"Unknown QBDI_ARCH : ${QBDI_ARCH}\"\n    exit 1\nfi\n\n"
  },
  {
    "path": "docker/ci_python_linux/img_build.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd $(dirname \"$0\")\nBASEDIR=$(pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\nif [[ \"${QBDI_ARCH}\" = \"X86_64\" ]]; then\n    DOCKER_IMG=\"quay.io/pypa/manylinux2014_x86_64\"\nelif [[ \"${QBDI_ARCH}\" = \"X86\" ]]; then\n    DOCKER_IMG=\"quay.io/pypa/manylinux2014_i686\"\nelif [[ \"${QBDI_ARCH}\" = \"AARCH64\" ]]; then\n    DOCKER_IMG=\"quay.io/pypa/manylinux2014_aarch64\"\nelse\n    echo \"Unknown QBDI_ARCH : ${QBDI_ARCH}\"\n    exit 1\nfi\n\n\ndocker build \"${BASEDIR}\" -t pyqbdi_build:base_${QBDI_ARCH} \\\n        --build-arg DOCKER_IMG=\"${DOCKER_IMG}\" \\\n        --build-arg USER_ID=\"$(id -u)\" \\\n        -f \"${BASEDIR}/Dockerfile\"\n\n"
  },
  {
    "path": "docker/ci_python_linux/whl_build.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd $(dirname \"$0\")\nBASEDIR=$(pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\nPYTHON_VERSION=\"${1:-all}\"\n\n./img_build.sh\n\ndocker run --rm \\\n    -e QBDI_PLATFORM=\"${QBDI_PLATFORM}\" \\\n    -e QBDI_ARCH=\"${QBDI_ARCH}\" \\\n    --mount type=bind,source=\"${GITDIR}\",target=/home/docker/qbdi \\\n    --mount type=bind,source=\"${HOME}/.ccache\",target=/home/docker/.ccache \\\n    pyqbdi_build:base_${QBDI_ARCH} \\\n    /bin/bash /home/docker/qbdi/docker/ci_python_linux/build_whl.sh \"${PYTHON_VERSION}\"\n\n"
  },
  {
    "path": "docker/common.sh",
    "content": "\nQBDI_VERSION=\"0.12.2-devel\"\nDOCKERHUB_REPO=\"qbdi/qbdi\"\nDOCKER_BUILD_DIR=\"/home/docker/qbdi/build\"\n\nDEBIAN_TARGET=\"trixie\"\nUBUNTU_LTS_TARGET=\"24.04\"\nUBUNTU_LAST_TARGET=\"25.10\"\n\nprepare_archive (){\n    pushd \"$(git rev-parse --show-toplevel)\" >/dev/null\n\n    git archive -o \"${BASEDIR}/qbdi.tar.gz\" --prefix=qbdi/ HEAD .\n\n    popd >/dev/null\n}\n\ndelete_archive() {\n    rm -f \"${BASEDIR}/qbdi.tar.gz\"\n}\n\n"
  },
  {
    "path": "docker/linux_arm_natif/.gitignore",
    "content": ".dockcross-linux-AARCH64-latest\n.dockcross-linux-ARM-latest\n\n"
  },
  {
    "path": "docker/linux_arm_natif/build_qbdi_linux.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd \"$(dirname \"$0\")\"\nBASEDIR=\"$(pwd -P)\"\nGITDIR=\"$(git rev-parse --show-toplevel)\"\n\nTAG_PREFIX=\"qbdi-linux/qbdi_test\"\nQBDI_PLATFORM=\"linux\"\nQBDI_ARCH=\"$1\"\n\nif [[ \"$QBDI_ARCH\" != \"AARCH64\" ]] && [[ \"$QBDI_ARCH\" != \"ARM\" ]]; then\n  echo \"Unrecognized architecture.\"\n  echo \"Supported : AARCH64, ARM\"\n  exit 1\nfi\n\n./images/build_docker_img.sh \"${QBDI_ARCH}\"\n\nIMG_BUILD=\"${TAG_PREFIX}:native_${QBDI_PLATFORM}_${QBDI_ARCH}\"\n\ndocker run -it --rm \\\n    -v \"${HOME}/.ccache:/home/docker/.ccache\" \\\n    -v \"${GITDIR}:/home/docker/QBDI\" \\\n    -e QBDI_PLATFORM=\"${QBDI_PLATFORM}\" \\\n    -e QBDI_ARCH=\"${QBDI_ARCH}\" \\\n    \"${IMG_BUILD}\" \\\n    /home/docker/QBDI/docker/linux_arm_natif/docker_internal_script/build-test-linux.sh\n\n"
  },
  {
    "path": "docker/linux_arm_natif/docker_internal_script/build-test-linux.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\nmkdir -p \"build-docker-native-${QBDI_PLATFORM}-${QBDI_ARCH}\"\ncd \"build-docker-native-${QBDI_PLATFORM}-${QBDI_ARCH}\"\n\n\ncmake -G Ninja \\\n  -DCMAKE_BUILD_TYPE=Release \\\n  -DQBDI_PLATFORM=\"${QBDI_PLATFORM}\" \\\n  -DQBDI_ARCH=\"${QBDI_ARCH}\" \\\n  -DQBDI_EXAMPLES=ON \\\n  -DQBDI_TOOLS_VALIDATOR=ON \\\n  -DQBDI_TOOLS_PYQBDI=ON \\\n  ..\n\nninja\n\n./test/QBDITest\n\ncpack\n\ncd ../tools/validation_runner\n#./ValidationRunner.py coverage.cfg -l \"../../build-docker-native-${QBDI_PLATFORM}-${QBDI_ARCH}/tools/validator/libvalidator.so\"\n\nexit 0\n"
  },
  {
    "path": "docker/linux_arm_natif/images/Dockerfile",
    "content": "ARG DOCKER_IMG=\"debian:latest\"\nFROM $DOCKER_IMG\n\nARG QBDI_ARCH=\"AARCH64\"\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    QBDI_PLATFORM=\"linux\" \\\n    CLICOLOR_FORCE=1\n\n# Get latest package list, upgrade packages, install required packages\n# and cleanup to keep container as small as possible\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n        adduser \\\n        bash \\\n        build-essential \\\n        ca-certificates \\\n        ccache \\\n        cmake \\\n        file \\\n        g++ \\\n        git \\\n        gzip \\\n        imagemagick \\\n        libstdc++-13-dev \\\n        ninja-build \\\n        openssl \\\n        pkg-config \\\n        python3 \\\n        python3-dev \\\n        python3-pip \\\n        python3-yaml \\\n        wget \\\n        xxd \\\n        zip \\\n        && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n# create a user\nRUN adduser --disabled-password --gecos '' --home \"$HOME\" \"$USER\"\n\n# switch to new user\nUSER $USER\n\nRUN mkdir \"$HOME/QBDI\"\n\nWORKDIR $HOME/QBDI\n"
  },
  {
    "path": "docker/linux_arm_natif/images/build_docker_img.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd \"$(dirname \"$0\")\"\nBASEDIR=\"$(pwd -P)\"\nTAG_PREFIX=\"qbdi-linux/qbdi_test\"\nTARGET_ARCH=\"$1\"\n\nif [[ -n \"$TARGET_ARCH\" ]] && [[ \"$TARGET_ARCH\" != \"AARCH64\" ]] && [[ \"$TARGET_ARCH\" != \"ARM\" ]]; then\n  echo \"Unrecognized architecture.\"\n  echo \"Supported : AARCH64, ARM\"\n  exit 1\nfi\n\nbuild_linux() {\n  QBDI_PLATFORM=\"linux\"\n  QBDI_ARCH=\"$1\"\n  if [[ \"$QBDI_ARCH\" = \"AARCH64\" ]]; then\n    BASE_IMG=\"arm64v8/debian:latest\"\n  elif [[ \"$QBDI_ARCH\" = \"ARM\" ]]; then\n    BASE_IMG=\"arm32v7/debian:latest\"\n  fi\n  docker build \"${BASEDIR}\" -f \"${BASEDIR}/Dockerfile\" -t \"${TAG_PREFIX}:native_${QBDI_PLATFORM}_${QBDI_ARCH}\" --build-arg DOCKER_IMG=\"${BASE_IMG}\"\n}\n\nif [[ -n \"$TARGET_ARCH\" ]]; then\n  build_linux \"$TARGET_ARCH\"\nelse\n  build_linux AARCH64\n  build_linux ARM\nfi\n\n"
  },
  {
    "path": "docker/python_linux_arm/docker_internal_script/build_wheel.sh",
    "content": "#!/usr/bin/env bash\n\n# change python host platform to armv7 arch\n# This is needed when compile on X86 or AARCH64 platform, inside a ARM docker\nexport _PYTHON_HOST_PLATFORM=\"linux-armv7l\"\n\n# run with --skip-dependency-check and --no-isolation to\n# avoid compile cmake.whl and ninja.whl that isn't prebuild for armv7\npython -m build --skip-dependency-check --no-isolation -w\n"
  },
  {
    "path": "docker/python_linux_arm/images/Dockerfile",
    "content": "ARG DOCKER_IMG=\"arm32v7/python:3\"\n\nFROM $DOCKER_IMG\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    QBDI_PLATFORM=\"linux\"\n\n# Get latest package list, upgrade packages, install required packages\n# and cleanup to keep container as small as possible\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n        adduser \\\n        bash \\\n        build-essential \\\n        ca-certificates \\\n        ccache \\\n        cmake \\\n        g++ \\\n        git \\\n        ninja-build \\\n        pkg-config \\\n        python3 \\\n        python3-dev \\\n        python3-pip \\\n        wget \\\n        xxd \\\n        zip \\\n        && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n# create a user\nRUN adduser --disabled-password --gecos '' --home \"$HOME\" \"$USER\"\n\n# switch to new user\nUSER $USER\n\nENV PATH=\"${PATH}:${HOME}/.local/bin\"\n\nRUN mkdir \"${HOME}/QBDI\" && \\\n    pip install --upgrade --user pip build setuptools\n\nWORKDIR $HOME/QBDI\n\nARG USER_ID=1000\n\n"
  },
  {
    "path": "docker/python_linux_arm/images/build_docker_img.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd \"$(dirname \"$0\")\"\nBASEDIR=\"$(pwd -P)\"\nTAG_PREFIX=\"pyqbdi/wheel_arm_building\"\nBASE_DEBIAN=\"trixie\"\nPYTHON_VERSION=\"$1\"\n\nbuild_python_image() {\n  QBDI_PLATFORM=\"linux\"\n  QBDI_ARCH=\"ARM\"\n  PYTHON_VERSION=\"$1\"\n  BASE_IMG=\"arm32v7/python:${PYTHON_VERSION}-${BASE_DEBIAN}\"\n  docker build \"${BASEDIR}\" -f \"${BASEDIR}/Dockerfile\" \\\n               --platform=\"linux/arm/v7\" \\\n               -t \"${TAG_PREFIX}:${QBDI_PLATFORM}_${QBDI_ARCH}_${PYTHON_VERSION}\" \\\n               --build-arg DOCKER_IMG=\"${BASE_IMG}\"\n}\n\nif [[ -n \"$PYTHON_VERSION\" ]]; then\n  build_python_image \"$PYTHON_VERSION\"\nelse\n  build_python_image 3.9\n  build_python_image 3.10\n  build_python_image 3.11\n  build_python_image 3.12\n  build_python_image 3.13\n  build_python_image 3.14\nfi\n\n\n"
  },
  {
    "path": "docker/python_linux_arm/whl_build.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\ncd $(dirname \"$0\")\nBASEDIR=$(pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\nTAG_PREFIX=\"pyqbdi/wheel_arm_building\"\nBASE_DEBIAN=\"trixie\"\nPYTHON_VERSION=\"$1\"\n\n./images/build_docker_img.sh \"${PYTHON_VERSION}\"\n\nbuild_wheel() {\n  QBDI_PLATFORM=\"linux\"\n  QBDI_ARCH=\"ARM\"\n  PYTHON_VERSION=\"$1\"\n\n  docker run --rm -it \\\n      --platform=\"linux/arm/v7\" \\\n      -e QBDI_PLATFORM=\"${QBDI_PLATFORM}\" \\\n      -e QBDI_ARCH=\"${QBDI_ARCH}\" \\\n      --mount type=bind,source=\"${GITDIR}\",target=/home/docker/QBDI \\\n      --mount type=bind,source=\"${HOME}/.ccache\",target=/home/docker/.ccache \\\n      \"${TAG_PREFIX}:${QBDI_PLATFORM}_${QBDI_ARCH}_${PYTHON_VERSION}\" \\\n      bash /home/docker/QBDI/docker/python_linux_arm/docker_internal_script/build_wheel.sh\n}\n\n\nif [[ -n \"$PYTHON_VERSION\" ]]; then\n  build_wheel \"$PYTHON_VERSION\"\nelse\n  build_wheel 3.9\n  build_wheel 3.10\n  build_wheel 3.11\n  build_wheel 3.12\n  build_wheel 3.13\n  build_wheel 3.14\nfi\n\n"
  },
  {
    "path": "docker/release.sh",
    "content": "#!/usr/bin/bash\n\nset -e\n\nHASHFILE=\"image.hash\"\nPUSH_IMAGE=0\nTARGET_ARCH=\"$1\"\n\nBASEDIR=$(cd $(dirname \"$0\") && pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\ntouch \"$HASHFILE\"\n. \"${BASEDIR}/common.sh\"\n\ndocker_login() {\n    if [[ \"${PUSH_IMAGE}\" -ne 0 ]]; then\n        docker login\n    fi\n}\n\npush_image() {\n    if [[ \"${PUSH_IMAGE}\" -ne 0 ]]; then\n        docker push \"$1\"\n    fi\n}\n\ndocker_logout() {\n    if [[ \"${PUSH_IMAGE}\" -ne 0 ]]; then\n        docker logout\n    fi\n}\n\npush_images() {\n    TAG=\"$1\"\n    shift\n    while [[ -n \"$1\" ]]; do\n        docker tag \"$TAG\" \"${DOCKERHUB_REPO}:$1\"\n        push_image \"${DOCKERHUB_REPO}:$1\"\n        if [[ \"$1\" != \"latest\" ]]; then\n            docker tag \"$TAG\" \"${DOCKERHUB_REPO}:${QBDI_VERSION}_$1\"\n            push_image \"${DOCKERHUB_REPO}:${QBDI_VERSION}_$1\"\n        fi\n        shift\n    done\n}\n\nprint_hash() {\n    IMG=\"${DOCKERHUB_REPO}:${QBDI_VERSION}_$1\"\n    echo -n \"${IMG} \" >> \"$HASHFILE\"\n    docker inspect --format='{{.Id}}' \"${IMG}\" >> \"$HASHFILE\"\n}\n\nperform_action() {\n    ACTION=\"$1\"; shift\n    ARCH=\"$1\"; shift\n    DOCKER_IMG_BASE=\"$1\"; shift\n\n    INTERNAL_DOCKER_TAG=\"qbdi:${ARCH}_${DOCKER_IMG_BASE%%:*}_${DOCKER_IMG_BASE##*:}\"\n    TARGET_DOCKER_TAG=\"$1\"\n\n    if [[ -n \"$TARGET_ARCH\" ]] && [[ \"$ARCH\" != \"$TARGET_ARCH\" ]]; then\n        return\n    fi\n\n    if [[ \"${ACTION}\" = \"build\" ]]; then\n        \"${BASEDIR}/ubuntu_debian/build.sh\" \"${ARCH}\" \"${DOCKER_IMG_BASE}\"\n    elif [[ \"${ACTION}\" = \"push\" ]]; then\n        push_images \"${INTERNAL_DOCKER_TAG}\" \"$@\"\n    elif [[ \"${ACTION}\" = \"hash\" ]]; then\n        print_hash \"${TARGET_DOCKER_TAG}\"\n    else\n        echo \"Unknown action ${ACTION}\"\n        exit 1\n    fi\n}\n\n\nperform_action_by_image() {\n    ACTION=\"$1\"\n\n    perform_action \"$ACTION\" \"ARM\" \"debian:${DEBIAN_TARGET}\" \"armv7_debian_${DEBIAN_TARGET}\" \"armv7_debian\" \"armv7\"\n    perform_action \"$ACTION\" \"AARCH64\" \"debian:${DEBIAN_TARGET}\" \"arm64_debian_${DEBIAN_TARGET}\" \"arm64_debian\" \"arm64\"\n    perform_action \"$ACTION\" \"X86\" \"debian:${DEBIAN_TARGET}\" \"x86_debian_${DEBIAN_TARGET}\" \"x86_debian\" \"x86\"\n    perform_action \"$ACTION\" \"X86_64\" \"debian:${DEBIAN_TARGET}\" \"x64_debian_${DEBIAN_TARGET}\" \"x64_debian\" \"x64\" \"latest\"\n\n    perform_action \"$ACTION\" \"ARM\" \"ubuntu:${UBUNTU_LTS_TARGET}\" \"armv7_ubuntu_${UBUNTU_LTS_TARGET}\" \"armv7_ubuntu_lts\" \"armv7_ubuntu\"\n    perform_action \"$ACTION\" \"AARCH64\" \"ubuntu:${UBUNTU_LTS_TARGET}\" \"arm64_ubuntu_${UBUNTU_LTS_TARGET}\" \"arm64_ubuntu_lts\" \"arm64_ubuntu\"\n    perform_action \"$ACTION\" \"X86_64\" \"ubuntu:${UBUNTU_LTS_TARGET}\" \"x64_ubuntu_${UBUNTU_LTS_TARGET}\" \"x64_ubuntu_lts\" \"x64_ubuntu\"\n\n    perform_action \"$ACTION\" \"ARM\" \"ubuntu:${UBUNTU_LAST_TARGET}\" \"armv7_ubuntu_${UBUNTU_LAST_TARGET}\"\n    perform_action \"$ACTION\" \"AARCH64\" \"ubuntu:${UBUNTU_LAST_TARGET}\" \"arm64_ubuntu_${UBUNTU_LAST_TARGET}\"\n    perform_action \"$ACTION\" \"X86_64\" \"ubuntu:${UBUNTU_LAST_TARGET}\" \"x64_ubuntu_${UBUNTU_LAST_TARGET}\"\n}\n\nperform_action_by_image \"build\"\ndocker_login\nperform_action_by_image \"push\"\ndocker_logout\nperform_action_by_image \"hash\"\n\ncat \"$HASHFILE\"\n\n"
  },
  {
    "path": "docker/ubuntu_debian/Dockerfile",
    "content": "ARG DOCKER_IMG=\"ubuntu:latest\"\n\nFROM $DOCKER_IMG AS builder\n\nARG QBDI_ARCH=\"X86_64\"\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    QBDI_PLATFORM=\"linux\"\n\n# setup backport to use cmake >= 3.28\nRUN ( cat /etc/debian_version | grep -v -q 12 ) || \\\n    ( echo 'deb http://deb.debian.org/debian bookworm-backports main contrib non-free' > /etc/apt/sources.list.d/backports.list )\n\n# Get latest package list, upgrade packages, install required packages\n# and cleanup to keep container as small as possible\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n        adduser \\\n        bash \\\n        build-essential \\\n        cmake \\\n        g++ \\\n        git \\\n        libstdc++-13-dev \\\n        ninja-build \\\n        pkg-config \\\n        wget \\\n        ca-certificates \\\n        python3 \\\n        python3-dev && \\\n    ( ( cat /etc/debian_version | grep -v -q 12 ) || ( apt-get install -y --no-install-recommends cmake/bookworm-backports ) ) && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n# create a user\nRUN adduser --disabled-password --gecos '' --home \"$HOME\" \"$USER\"\n\n# build / test / install QBDI\nARG CMAKE_ARGUMENT=\"\"\n\n# git archive -o qbdi.tar.gz --prefix=qbdi/ HEAD .\nADD qbdi.tar.gz $HOME/\n\nWORKDIR $HOME/qbdi\n\nRUN chown -R $USER:$USER .\n\n# switch to new user\nUSER $USER\n\nRUN mkdir build && \\\n    cd build && \\\n    cmake -G Ninja \\\n          -DCMAKE_BUILD_TYPE=Release \\\n          -DCMAKE_CROSSCOMPILING=FALSE \\\n          -DQBDI_PLATFORM=$QBDI_PLATFORM \\\n          -DQBDI_ARCH=$QBDI_ARCH \\\n          -DCMAKE_INSTALL_PREFIX=$PREFIX \\\n          -DQBDI_TOOLS_PYQBDI=OFF \\\n          -DQBDI_CCACHE=OFF \\\n          $CMAKE_ARGUMENT \\\n          ../ && \\\n    ninja && \\\n    # test\n    ./test/QBDITest && \\\n    # create package and install\n    rm -f QBDI-*-$QBDI_PLATFORM.deb && \\\n    cpack -G DEB\n\n\nFROM $DOCKER_IMG\n\nWORKDIR /root\n\nCOPY --from=builder /home/docker/qbdi/build/*.deb .\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y ./*.deb && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\nCMD [\"/bin/bash\"]\n"
  },
  {
    "path": "docker/ubuntu_debian/build.sh",
    "content": "#!/usr/bin/bash\n\nset -e\nset -x\n\nBASEDIR=$(cd $(dirname \"$0\") && pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\n. \"${BASEDIR}/../common.sh\"\n\nARCH=\"X86_64\"\nDOCKER_IMG=\"ubuntu\"\nTAG=\"latest\"\nDOCKER_PLATFORM=\"linux/amd64\"\n\nif [[ -n \"$2\" ]]; then\n    TAG=\"${2##*:}\"\n    DOCKER_IMG=\"${2%%:*}\"\nfi\n\nif [[ \"$1\" = \"X86\" ]] || [[ \"$1\" = \"x86\" ]]; then\n    ARCH=\"X86\"\n    DOCKER_IMG=\"i386/$DOCKER_IMG\"\n    DOCKER_PLATFORM=\"linux/386\"\nelif [[ \"$1\" = \"ARM\" ]] || [[ \"$1\" = \"arm\" ]] || [[ \"$1\" = \"arm32\" ]]; then\n    ARCH=\"ARM\"\n    DOCKER_IMG=\"arm32v7/$DOCKER_IMG\"\n    DOCKER_PLATFORM=\"linux/arm/v7\"\nelif [[ \"$1\" = \"AARCH64\" ]] || [[ \"$1\" = \"aarch64\" ]] || [[ \"$1\" = \"arm64\" ]]; then\n    ARCH=\"AARCH64\"\n    DOCKER_IMG=\"arm64v8/$DOCKER_IMG\"\n    DOCKER_PLATFORM=\"linux/arm64/v8\"\nfi\n\nCMAKE_ARGUMENT=\"$3\"\n\nDISTRIB=\"${DOCKER_IMG##*/}\"\n\nDOCKER_TAG=\"qbdi:${ARCH}_${DOCKER_IMG##*/}_${TAG}\"\n\nDOCKERFILE=\"${BASEDIR}/Dockerfile\"\n\nprepare_archive\n\ndocker build \"${BASEDIR}\" -t \"${DOCKER_TAG}\" -f \"${DOCKERFILE}\" \\\n             --platform=\"$DOCKER_PLATFORM\" \\\n             --build-arg DOCKER_IMG=\"${DOCKER_IMG}:${TAG}\" \\\n             --build-arg QBDI_ARCH=\"$ARCH\" \\\n             --build-arg CMAKE_ARGUMENT=\"${CMAKE_ARGUMENT}\"\n\ndelete_archive\n\necho \"Success build ${DOCKER_TAG}\"\n"
  },
  {
    "path": "docker/ubuntu_debian_devel/Dockerfile",
    "content": "ARG DOCKER_IMG=\"ubuntu:latest\"\n\nFROM $DOCKER_IMG\n\nARG QBDI_ARCH=\"X86_64\"\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\" \\\n    PREFIX=\"/usr\" \\\n    QBDI_PLATFORM=\"linux\"\n\n# Get latest package list, upgrade packages, install required packages\n# and cleanup to keep container as small as possible\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n        adduser \\\n        bash \\\n        sudo \\\n        build-essential \\\n        cmake \\\n        g++ \\\n        git \\\n        libstdc++-13-dev \\\n        ninja-build \\\n        pkg-config \\\n        wget \\\n        ca-certificates \\\n        python3 \\\n        python3-dev \\\n        python3-yaml \\\n        imagemagick \\\n        zip \\\n        less \\\n        file \\\n        xxd \\\n        git && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n# create a user and add user in sudo to ease debug\nRUN adduser --disabled-password --gecos '' --home \"$HOME\" \"$USER\" && \\\n    adduser $USER sudo && \\\n    echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers\n\n# build / test / install QBDI\nARG CMAKE_ARGUMENT=\"\"\n\n# git archive -o qbdi.tar.gz --prefix=qbdi/ HEAD .\nADD qbdi.tar.gz $HOME/\n\nWORKDIR $HOME/qbdi\n\nRUN chown -R $USER:$USER .\n\n# switch to new user\nUSER $USER\n\nRUN mkdir build && \\\n    cd build && \\\n    cmake -G Ninja \\\n          -DCMAKE_BUILD_TYPE=Release \\\n          -DCMAKE_CROSSCOMPILING=FALSE \\\n          -DQBDI_PLATFORM=$QBDI_PLATFORM \\\n          -DQBDI_ARCH=$QBDI_ARCH \\\n          -DCMAKE_INSTALL_PREFIX=$PREFIX \\\n          -DQBDI_CCACHE=OFF \\\n          -DQBDI_LOG_DEBUG=ON \\\n          -DQBDI_EXAMPLES=ON \\\n          -DQBDI_TOOLS_PYQBDI=ON \\\n          -DQBDI_TOOLS_VALIDATOR=ON \\\n          $CMAKE_ARGUMENT \\\n          ../ && \\\n    ninja && \\\n    # test\n    ./test/QBDITest && \\\n    # create package and install\n    rm -f QBDI-*-$QBDI_PLATFORM.deb && \\\n    cpack -G DEB && \\\n    sudo dpkg -i QBDI-*-$QBDI_PLATFORM-$QBDI_ARCH.deb\n\nWORKDIR $HOME\n\nCMD [\"/bin/bash\"]\n"
  },
  {
    "path": "docker/ubuntu_debian_devel/build.sh",
    "content": "#!/usr/bin/bash\n\nset -e\n\nBASEDIR=$(cd $(dirname \"$0\") && pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\n. \"${BASEDIR}/../common.sh\"\n\nARCH=\"X86_64\"\nDOCKER_IMG=\"ubuntu\"\nTAG=\"latest\"\nDOCKER_PLATFORM=\"linux/amd64\"\n\nif [[ -n \"$2\" ]]; then\n    TAG=\"${2##*:}\"\n    DOCKER_IMG=\"${2%%:*}\"\nfi\n\nif [[ \"$1\" = \"X86\" || \"$1\" = \"x86\" ]]; then\n    ARCH=\"X86\"\n    DOCKER_IMG=\"i386/$DOCKER_IMG\"\n    DOCKER_PLATFORM=\"linux/386\"\nelif [[ \"$1\" = \"ARM\" ]] || [[ \"$1\" = \"arm\" ]] || [[ \"$1\" = \"arm32\" ]]; then\n    ARCH=\"ARM\"\n    DOCKER_IMG=\"arm32v7/$DOCKER_IMG\"\n    DOCKER_PLATFORM=\"linux/arm/v7\"\nelif [[ \"$1\" = \"AARCH64\" ]] || [[ \"$1\" = \"aarch64\" ]] || [[ \"$1\" = \"arm64\" ]]; then\n    ARCH=\"AARCH64\"\n    DOCKER_IMG=\"arm64v8/$DOCKER_IMG\"\n    DOCKER_PLATFORM=\"linux/arm64/v8\"\nfi\n\nCMAKE_ARGUMENT=\"$3\"\n\nDISTRIB=\"${DOCKER_IMG##*/}\"\n\nDOCKER_TAG=\"qbdi:${ARCH}_${DOCKER_IMG##*/}_${TAG}_devel\"\n\nDOCKERFILE=\"${BASEDIR}/Dockerfile\"\n\nprepare_archive\n\ndocker build \"${BASEDIR}\" -t \"${DOCKER_TAG}\" \\\n             --platform=\"$DOCKER_PLATFORM\" \\\n             --build-arg DOCKER_IMG=\"${DOCKER_IMG}:${TAG}\" \\\n             --build-arg QBDI_ARCH=\"$ARCH\" \\\n             --build-arg CMAKE_ARGUMENT=\"$CMAKE_ARGUMENT\"\n\ndelete_archive\n\necho \"Success build ${DOCKER_TAG}\"\n"
  },
  {
    "path": "docs/CMakeLists.txt",
    "content": "configure_file(qbdi_cpp.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/qbdi_cpp.doxygen\n               @ONLY)\nconfigure_file(qbdi_c.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/qbdi_c.doxygen\n               @ONLY)\nconfigure_file(qbdipreload.doxygen.in\n               ${CMAKE_CURRENT_BINARY_DIR}/qbdipreload.doxygen @ONLY)\n\nadd_custom_target(docs DEPENDS docs-doxygen docs-sphinx)\n\nadd_custom_target(\n  docs-doxygen\n  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n  COMMAND doxygen qbdi_cpp.doxygen\n  COMMAND doxygen qbdi_c.doxygen\n  COMMAND doxygen qbdipreload.doxygen)\n\nadd_custom_target(\n  docs-install-jsdoc\n  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n  COMMAND npm install jsdoc)\n\nexecute_process(COMMAND npm install jsdoc\n                WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})\nexecute_process(\n  COMMAND npm root\n  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n  OUTPUT_VARIABLE NPM_JSDOC_ROOT\n  OUTPUT_STRIP_TRAILING_WHITESPACE)\n\nadd_custom_target(\n  docs-sphinx\n  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source\n  DEPENDS docs-doxygen pyqbdi\n  COMMAND\n    ${CMAKE_COMMAND} -E env\n    \"PYTHONPATH=${PROJECT_BINARY_DIR}/docs:$ENV{PYTHONPATH}\"\n    \"SPHINX_JS_NODE_MODULES=${NPM_JSDOC_ROOT}\"\n    \"QBDI_DOXYGEN_DIRS=${CMAKE_CURRENT_BINARY_DIR}\" sphinx-build -a -b html .\n    \"${CMAKE_CURRENT_BINARY_DIR}/build\")\n\nadd_custom_target(\n  docs-sphinx-fast\n  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/source\n  DEPENDS pyqbdi\n  COMMAND\n    ${CMAKE_COMMAND} -E env\n    \"PYTHONPATH=${PROJECT_BINARY_DIR}/docs:$ENV{PYTHONPATH}\"\n    \"QBDI_DOXYGEN_DIRS=${CMAKE_CURRENT_BINARY_DIR}\" sphinx-build -a -b html .\n    \"${CMAKE_CURRENT_BINARY_DIR}/build\")\n"
  },
  {
    "path": "docs/qbdi_c.doxygen.in",
    "content": "# Doxyfile 1.9.2\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = QBDI\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = @QBDI_VERSION_MAJOR@.@QBDI_VERSION_MINOR@.@QBDI_VERSION_PATCH@\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = @CMAKE_CURRENT_BINARY_DIR@/doxygen_c\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      = in=C\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = YES\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use\n# during processing. When set to 0 doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and MacOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete\n# function parameter documentation. If set to NO, doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the doxygen process doxygen will return with a non-zero status.\n# Possible values are: NO, YES and FAIL_ON_WARNINGS.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = @CMAKE_CURRENT_SOURCE_DIR@/../include \\\n                         @CMAKE_CURRENT_BINARY_DIR@/../include\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,\n# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C\n# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,\n# *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cpp \\\n                         *.h \\\n                         *.hpp\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                = @CMAKE_CURRENT_SOURCE_DIR@/../include/QBDI/VM.h \\\n                         @CMAKE_CURRENT_SOURCE_DIR@/../include/QBDI/Memory.hpp \\\n                         @CMAKE_CURRENT_SOURCE_DIR@/../include/QBDI/arch\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = NO\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATOR_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = YES\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = YES\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = YES\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             = QBDI_ARCH_X86_64 \\\n                         QBDI_ARCH_X86 \\\n                         QBDI_ARCH_ARM \\\n                         QBDI_ARCH_AARCH64 \\\n                         QBDI_EXPORT= \\\n                         \"_QBDI_EI(x)=QBDI_##x\"\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      = _QBDI_EI\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will wrapped across multiple lines. Some heuristics are apply\n# to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# http://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "docs/qbdi_cpp.doxygen.in",
    "content": "# Doxyfile 1.9.2\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = QBDI\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = @QBDI_VERSION_MAJOR@.@QBDI_VERSION_MINOR@.@QBDI_VERSION_PATCH@\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = @CMAKE_CURRENT_BINARY_DIR@/doxygen_cpp\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      = in=C++\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = YES\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use\n# during processing. When set to 0 doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and MacOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete\n# function parameter documentation. If set to NO, doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the doxygen process doxygen will return with a non-zero status.\n# Possible values are: NO, YES and FAIL_ON_WARNINGS.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = @CMAKE_CURRENT_SOURCE_DIR@/../include \\\n                         @CMAKE_CURRENT_SOURCE_DIR@/../src \\\n                         @CMAKE_CURRENT_BINARY_DIR@/../include\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,\n# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C\n# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,\n# *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cpp \\\n                         *.h \\\n                         *.hpp\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                = @CMAKE_CURRENT_SOURCE_DIR@/../include/QBDI/VM_C.h \\\n                         @CMAKE_CURRENT_SOURCE_DIR@/../include/QBDI/Memory.h \\\n                         @CMAKE_CURRENT_SOURCE_DIR@/../include/QBDI/arch \\\n                         @CMAKE_CURRENT_SOURCE_DIR@/../src/Patch/ARM/\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = NO\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATOR_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = YES\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = YES\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = YES\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             = QBDI_ARCH_X86_64 \\\n                         QBDI_ARCH_X86 \\\n                         QBDI_ARCH_ARM \\\n                         QBDI_ARCH_AARCH64 \\\n                         __cplusplus \\\n                         QBDI_EXPORT= \\\n                         _QBDI_EI(x)=x\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      = _QBDI_EI\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will wrapped across multiple lines. Some heuristics are apply\n# to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# http://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "docs/qbdipreload.doxygen.in",
    "content": "# Doxyfile 1.9.2\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = QBDI\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = @QBDI_VERSION_MAJOR@.@QBDI_VERSION_MINOR@.@QBDI_VERSION_PATCH@\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = @CMAKE_CURRENT_BINARY_DIR@/doxygen_qbdipreload\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = YES\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use\n# during processing. When set to 0 doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and MacOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete\n# function parameter documentation. If set to NO, doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the doxygen process doxygen will return with a non-zero status.\n# Possible values are: NO, YES and FAIL_ON_WARNINGS.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = @CMAKE_CURRENT_SOURCE_DIR@/../tools/QBDIPreload/include\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,\n# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C\n# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,\n# *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.h\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = NO\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATOR_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = YES\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = YES\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = YES\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             = QBDI_EXPORT=\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      = _QBDI_EI\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will wrapped across multiple lines. Some heuristics are apply\n# to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# http://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "python-dateutil\npygit2\nrequests\nSphinx==8.2.3\ndocutils==0.21.2\nbreathe==4.36.0\nsphinx-js==5.0.2\nmarkupsafe==3.0.3\nsphinx_rtd_theme==3.0.2\n"
  },
  {
    "path": "docs/rtd_pyqbdi_artifact/setup.py",
    "content": "#!/usr/bin/env python3\n\n# This script will install the latest artifact from 'PyQBDI Linux package' github action\n\nimport dateutil.parser\nimport io\nimport os\nimport os.path\nimport platform\nfrom pygit2 import Repository\nimport re\nimport requests\nimport subprocess\nimport sys\nimport time\nimport zipfile\n\nbase_url = \"https://api.github.com/repos/QBDI/QBDI\"\ndefault_headers = {\"Accept\":\"application/vnd.github.v3+json\",\n                   \"X-GitHub-Api-Version\":\"2022-11-28\",\n                   \"Authorization\":\"token {}\".format(os.environ.get('GITHUB_TOKEN')) }\nworkflow_name = \"PyQBDI package\"\ndefault_per_page = 100\n\ndef do_get_request(path, params={}, headers={}, retry=10, retry_sleep=1, binary=False):\n\n    for i in range(retry):\n        r = requests.get(base_url + path, params=params, headers={**default_headers, **headers})\n        if r.status_code == 200:\n            success = True\n            break\n        else:\n            print('[-] Fail to get {}: received {}'.format(base_url + path, r.status_code))\n            success = False\n            if i < retry - 1:\n                time.sleep(retry_sleep)\n    r.raise_for_status()\n    if binary:\n        return r.content\n    return r.json()\n\ndef get_workflow_id(name):\n\n    nb_workflows = 1\n    workflows = []\n    page = 0\n    while len(workflows) != nb_workflows:\n        page += 1\n        d = do_get_request(\"/actions/workflows\", params={\"per_page\": default_per_page, \"page\": page})\n        nb_workflows = d[\"total_count\"]\n        workflows += d[\"workflows\"]\n\n    workflow_id = -1\n    for w in workflows:\n        if w['name'] == name:\n            print(f\"[+] Found workflow_id {w['id']} for workflow '{name}'\")\n            return w['id']\n\n    assert False, f\"Cannot found workflow '{name}'\"\n\ndef get_last_workflow_run(ident, artifact_name, branch=None, commit_hash=None):\n\n    nb_runs = 1\n    runs = []\n    page = 0\n    while len(runs) != nb_runs:\n        page += 1\n        p = {\"per_page\": default_per_page, \"page\": page}\n        if branch != None:\n            p['branch'] = branch\n        d = do_get_request(f\"/actions/workflows/{ident}/runs\", params=p)\n        nb_runs = d[\"total_count\"]\n        runs += d[\"workflow_runs\"]\n\n    if runs == []:\n        print(f\"[-] No runs found for workflow id={ident} with branch='{branch}'\")\n        return None\n\n    print(f\"[+] {len(runs)} runs found for workflow id={ident} with branch='{branch}'\")\n\n    last_run = None\n    for r in runs:\n        if r['event'] not in ['push', 'workflow_dispatch']:\n            continue\n        if commit_hash != None and r['head_sha'] != commit_hash:\n            continue\n        if last_run is not None and dateutil.parser.isoparse(last_run['created_at']) > dateutil.parser.isoparse(r['created_at']):\n            continue\n        if ((r['status'] == 'completed' and r['conclusion'] == 'success') or\n            get_artifact(r['id'], artifact_name, check=True) is not None):\n\n            last_run = r\n\n    if last_run == None:\n        print(f\"[-] no complete run for branch='{branch}' commit_hash={commit_hash}\")\n        return None\n\n    print(f\"[+] found run {last_run['id']} create at {last_run['created_at']}\")\n    return last_run\n\ndef get_artifact(run_id, artifact_name, check=False):\n    nb_artifacts = 1\n    artifacts = []\n    page = 0\n\n    i = 1\n    while len(artifacts) != nb_artifacts:\n        page += 1\n        d = do_get_request(f\"/actions/runs/{run_id}/artifacts\", params={\"per_page\": default_per_page, \"page\": page})\n        nb_artifacts = d[\"total_count\"]\n        artifacts += d[\"artifacts\"]\n\n    for artifact in artifacts:\n        if artifact['name'] == artifact_name:\n            print(f\"[+] Found artifact {artifact['id']} create at {artifact['created_at']}\")\n            return artifact\n\n    assert check, f\"Cannot found artifact '{artifact_name}' in run {run_id}\"\n    return None\n\ndef download_wheel(artifact):\n\n    assert not artifact['expired'], f\"artifact {artifact['id']} as expired\"\n\n    art = do_get_request(f\"/actions/artifacts/{artifact['id']}/zip\", binary=True)\n\n    name_regex = re.compile(\"^PyQBDI-.*-cp{}{}-.*\\\\.whl$\".format(sys.version_info.major, sys.version_info.minor), re.IGNORECASE)\n    with zipfile.ZipFile(io.BytesIO(art)) as zip_archive:\n        for f in zip_archive.infolist():\n            if bool(name_regex.match(f.filename)):\n                print(f'[+] extract {f.filename}')\n                with zip_archive.open(f, 'r') as f_wheel:\n                    return f.filename, f_wheel.read()\n\n    assert False, f\"Cannot found wheel that match {name_regex.pattern}\"\n\ndef install_wheel(wheelname, wheeldata):\n\n    wheel_path = os.path.join(os.path.dirname(__file__), wheelname)\n    with open(wheel_path, 'wb') as f:\n        f.write(wheeldata)\n\n    print(f'[+] install {wheel_path}')\n\n    subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", wheel_path])\n\ndef check_installation():\n    print(f'[+] test installation')\n    subprocess.check_call([sys.executable, \"-c\", \"import pyqbdi; print(pyqbdi.__version__)\"])\n\ndef install_pyqbdi():\n\n    if hasattr(sys.implementation, \"_multiarch\"):\n        if '-' in sys.implementation._multiarch:\n            base_arch, base_os = sys.implementation._multiarch.split('-')[:2]\n        else:\n            base_arch = platform.machine()\n            base_os = sys.implementation._multiarch\n    else:\n        base_arch = platform.machine()\n        base_os = platform.system()\n\n    base_arch = base_arch.lower()\n    base_os = base_os.lower()\n\n    print(f\"[+] Executing installation on {base_arch} {base_os}\")\n\n    if base_os != 'linux':\n        print(f\"[!] Not supported build for {base_os} here\")\n        exit(1)\n\n    if base_arch in ['amd64', 'amd', 'x64', 'x86_64', 'x86', 'i386', 'i686']:\n        if sys.maxsize > 2**32:\n            artifact_name = \"PyQBDI_linux_X86_64_python_{}.{}\".format(sys.version_info.major, sys.version_info.minor)\n        else:\n            artifact_name = \"PyQBDI_linux_X86_python_{}.{}\".format(sys.version_info.major, sys.version_info.minor)\n    else:\n        print(f\"[!] Not supported build for {base_arch} in github CI\")\n        exit(1)\n\n    git_repo = Repository('.')\n    current_branch = os.environ.get('READTHEDOCS_VERSION', git_repo.head.shorthand)\n    if current_branch in ['latest', 'stable']:\n        current_branch = 'master'\n    current_hash = str(git_repo.head.target)\n\n    workflow_ID = get_workflow_id(workflow_name)\n\n    # search a run with this hash commit on this branch\n    run = get_last_workflow_run(workflow_ID, artifact_name, branch=current_branch, commit_hash=current_hash)\n    if run == None:\n        # search a run with this hash commit (branch filter isn't not always associated with a run)\n        run = get_last_workflow_run(workflow_ID, artifact_name, branch=None, commit_hash=current_hash)\n    if run == None:\n        # search a run on this branch\n        run = get_last_workflow_run(workflow_ID, artifact_name, branch=current_branch)\n    if run == None and current_branch != \"dev-next\":\n        # search a run on dev-next\n        run = get_last_workflow_run(workflow_ID, artifact_name, \"dev-next\")\n\n    assert run != None, f\"No compatible runs found for workflow {workflow_name} (id={workflow_ID})\"\n\n    artifact = get_artifact(run['id'], artifact_name)\n    wheelname, wheeldata = download_wheel(artifact)\n\n    install_wheel(wheelname, wheeldata)\n\n    check_installation()\n\nif __name__ == \"__main__\":\n    install_pyqbdi()\n\n"
  },
  {
    "path": "docs/source/_static/style.css",
    "content": ".green {\n    color: green;\n}\n.yellow {\n    color: #ffc000;\n}\n.orange {\n    color: #ff6000;\n}\n.red {\n    color: red;\n}\n\n.wy-nav-content {\n    max-width: none;\n}\n"
  },
  {
    "path": "docs/source/_templates/layout.html",
    "content": "{% extends \"!layout.html\" %}\n{% set css_files = css_files + [\"_static/style.css\"] %}\n"
  },
  {
    "path": "docs/source/api.rst",
    "content": "\nAPI\n===\n\nThis section contains the documentation of every QBDI API.\n\n\n.. toctree::\n    :maxdepth: 2\n\n    API description         <api_description>\n    C API                   <api_c>\n    C++ API                 <api_cpp>\n    PyQBDI API              <api_pyqbdi>\n    Frida/QBDI API          <api_js>\n    QBDIPreload API         <api_qbdipreload>\n    PyQBDIPreload API       <api_pyqbdipreload>\n\n"
  },
  {
    "path": "docs/source/api_c.rst",
    "content": ".. highlight:: c\n\nC API\n=====\n\nIntroduction\n------------\n\nThe C API offers bindings over the C++ API. The VM class' methods are replaced by C functions that receive an object of type ``VMInstanceRef`` as a first parameter.\n\nThis API is compatible with :ref:`QBDIPreload <qbdipreload_api>` on Linux and macOS.\n\nVM object\n---------\n\n..\n   Doc notes\n   Breathe doesn't have an option to force the C domain for project QBDI_C.\n   The available options allow forcing a domain per file, but some headers are shared between C and CPP API.\n\n   C API will be in the CPP domain of sphynx, but without the QBDI:: namespace.\n\n.. cpp:type:: VMInstanceRef\n\n    An abstract pointer to the VM object.\n\n.. doxygenfunction:: qbdi_initVM\n   :project: QBDI_C\n\n.. doxygenfunction:: qbdi_terminateVM\n   :project: QBDI_C\n\nOptions\n+++++++\n\n.. doxygenfunction:: qbdi_getOptions\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setOptions\n    :project: QBDI_C\n\n.. _state-management-c:\n\nState management\n++++++++++++++++\n\n.. doxygenfunction:: qbdi_getGPRState\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getFPRState\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getErrno\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setGPRState\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setFPRState\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setErrno\n    :project: QBDI_C\n\n.. _instrumentation-range-c:\n\nInstrumentation range\n+++++++++++++++++++++\n\nAddition\n^^^^^^^^\n\n.. doxygenfunction:: qbdi_addInstrumentedRange\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addInstrumentedModule\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addInstrumentedModuleFromAddr\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_instrumentAllExecutableMaps\n    :project: QBDI_C\n\nRemoval\n^^^^^^^\n\n.. doxygenfunction:: qbdi_removeInstrumentedRange\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_removeInstrumentedModule\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_removeInstrumentedModuleFromAddr\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_removeAllInstrumentedRanges\n    :project: QBDI_C\n\nCallback management\n+++++++++++++++++++\n\n.. _instcallback-management-c:\n\nInstCallback\n^^^^^^^^^^^^\n\n.. doxygenfunction:: qbdi_addCodeCB\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addCodeAddrCB\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addCodeRangeCB\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addMnemonicCB\n    :project: QBDI_C\n\n.. _vmcallback-management-c:\n\nVMEvent\n^^^^^^^\n\n.. doxygenfunction:: qbdi_addVMEventCB\n    :project: QBDI_C\n\n.. _memorycallback-management-c:\n\nMemoryAccess\n^^^^^^^^^^^^\n\n.. doxygenfunction:: qbdi_addMemAccessCB\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addMemAddrCB\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addMemRangeCB\n    :project: QBDI_C\n\n.. _instrrulecallback-management-c:\n\nInstrRuleCallback\n^^^^^^^^^^^^^^^^^\n\n.. doxygenfunction:: qbdi_addInstrRule\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addInstrRuleRange\n    :project: QBDI_C\n\nRemoval\n^^^^^^^\n\n.. doxygenfunction:: qbdi_deleteInstrumentation\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_deleteAllInstrumentations\n    :project: QBDI_C\n\nRun\n+++\n\n.. doxygenfunction:: qbdi_run\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_call\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_callA\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_callV\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_switchStackAndCall\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_switchStackAndCallA\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_switchStackAndCallV\n    :project: QBDI_C\n\n.. _instanalysis-getter-c:\n\nInstAnalysis\n++++++++++++\n\n.. doxygenfunction:: qbdi_getInstAnalysis\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getCachedInstAnalysis\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getJITInstAnalysis\n    :project: QBDI_C\n\n.. _memaccess-getter-c:\n\nMemoryAccess\n++++++++++++\n\n.. doxygenfunction:: qbdi_getInstMemoryAccess\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getBBMemoryAccess\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_recordMemoryAccess\n    :project: QBDI_C\n\nCache management\n++++++++++++++++\n\n.. doxygenfunction:: qbdi_precacheBasicBlock\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_clearCache\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_clearAllCache\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getNbExecBlock\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_reduceCacheTo\n    :project: QBDI_C\n\n.. _register-state-c:\n\nRegister state\n--------------\n\n.. cpp:type:: rword\n\n    An integer of the size of a register\n\n    - uint32_t for X86 and ARM\n    - uint64_t for X86_64 and AARCH64\n\n.. cpp:struct:: GPRState\n\n    General Purpose Register context.\n\n    For X86 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86/State.h\n       :start-after: SPHINX_X86_GPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_GPRSTATE_END\n       :code:\n\n    For X86_64 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86_64/State.h\n       :start-after: SPHINX_X86_64_GPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_64_GPRSTATE_END\n       :code:\n\n    For ARM architecture:\n\n    .. include:: ../../include/QBDI/arch/ARM/State.h\n       :start-after: SPHINX_ARM_GPRSTATE_BEGIN\n       :end-before: // SPHINX_ARM_GPRSTATE_END\n       :code:\n\n    For AARCH64 architecture:\n\n    .. include:: ../../include/QBDI/arch/AARCH64/State.h\n       :start-after: SPHINX_AARCH64_GPRSTATE_BEGIN\n       :end-before: // SPHINX_AARCH64_GPRSTATE_END\n       :code:\n\n.. cpp:struct:: FPRState\n\n    Floating Point Register context.\n\n    For X86 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86/State.h\n       :start-after: SPHINX_X86_FPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_FPRSTATE_END\n       :code:\n\n    For X86_64 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86_64/State.h\n       :start-after: SPHINX_X86_64_FPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_64_FPRSTATE_END\n       :code:\n\n    For ARM architecture:\n\n    .. include:: ../../include/QBDI/arch/ARM/State.h\n       :start-after: SPHINX_ARM_FPRSTATE_BEGIN\n       :end-before: // SPHINX_ARM_FPRSTATE_END\n       :code:\n\n    For AARCH64 architecture:\n\n    .. include:: ../../include/QBDI/arch/AARCH64/State.h\n       :start-after: SPHINX_AARCH64_FPRSTATE_BEGIN\n       :end-before: // SPHINX_AARCH64_FPRSTATE_END\n       :code:\n\n.. doxygenstruct:: MMSTReg\n    :project: QBDI_C\n    :members:\n    :undoc-members:\n\n.. doxygenstruct:: FPControl\n    :project: QBDI_C\n    :members:\n    :undoc-members:\n\n.. doxygenstruct:: FPStatus\n    :project: QBDI_C\n    :members:\n    :undoc-members:\n\n.. data:: REG_RETURN\n\n.. data:: REG_BP\n\n.. data:: REG_SP\n\n.. data:: REG_PC\n\n.. data:: NUM_GPR\n\n.. _callback-c:\n\nCallback\n--------\n\n.. doxygentypedef:: InstCallback\n    :project: QBDI_C\n\n.. doxygentypedef:: VMCallback\n    :project: QBDI_C\n\n.. doxygentypedef:: InstrRuleCallbackC\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_addInstrRuleData\n    :project: QBDI_C\n\n.. cpp:type:: InstrRuleDataVec\n\n    An abstract type to append InstCallback for the current instruction\n\n.. doxygenenum:: InstPosition\n    :project: QBDI_C\n\n.. doxygenenum:: CallbackPriority\n    :project: QBDI_C\n\n.. doxygenenum:: VMAction\n    :project: QBDI_C\n\n.. _instanalysis-c:\n\nInstAnalysis\n------------\n\n.. doxygenenum:: AnalysisType\n    :project: QBDI_C\n\n.. doxygenstruct:: InstAnalysis\n    :project: QBDI_C\n    :members:\n\n.. doxygenenum:: ConditionType\n    :project: QBDI_C\n\n.. doxygenstruct:: OperandAnalysis\n    :project: QBDI_C\n    :members:\n\n.. doxygenenum:: OperandType\n    :project: QBDI_C\n\n.. doxygenenum:: OperandFlag\n    :project: QBDI_C\n\n.. doxygenenum:: RegisterAccessType\n    :project: QBDI_C\n\n.. _memaccess-c:\n\nMemoryAccess\n------------\n\n.. doxygenstruct:: MemoryAccess\n    :project: QBDI_C\n    :members:\n\n.. doxygenenum:: MemoryAccessType\n    :project: QBDI_C\n\n.. doxygenenum:: MemoryAccessFlags\n    :project: QBDI_C\n\n.. _vmevent-c:\n\nVMEvent\n-------\n\n.. doxygenenum:: VMEvent\n    :project: QBDI_C\n\n.. doxygenstruct:: VMState\n    :project: QBDI_C\n    :members:\n\nMemory management\n-----------------\n\nAllocation\n++++++++++\n\n.. doxygenfunction:: qbdi_alignedAlloc\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_allocateVirtualStack\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_alignedFree\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_simulateCall\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_simulateCallV\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_simulateCallA\n    :project: QBDI_C\n\nExploration\n+++++++++++\n\n.. doxygenfunction:: qbdi_getModuleNames\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getCurrentProcessMaps\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_getRemoteProcessMaps\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_freeMemoryMapArray\n    :project: QBDI_C\n\n.. doxygenstruct:: qbdi_MemoryMap\n    :project: QBDI_C\n    :members: start, end, permission, name\n\n.. doxygenenum:: qbdi_Permission\n    :project: QBDI_C\n\nOther globals\n-------------\n\n.. cpp:enum:: Options\n\n  Note: some value are available only for some architecture\n\n  Values for all architecture :\n\n  .. cpp:enumerator:: NO_OPT\n\n      Default Value\n\n  .. cpp:enumerator:: OPT_DISABLE_FPR\n\n      Disable all operation on FPU (SSE, AVX, SIMD). May break the execution if the target use the FPU\n\n  .. cpp:enumerator:: OPT_DISABLE_OPTIONAL_FPR\n\n      Disable context switch optimisation when the target execblock doesn't used FPR\n\n  .. cpp:enumerator:: OPT_DISABLE_MEMORYACCESS_VALUE\n\n      Don't load memory access value\n\n  .. cpp:enumerator:: OPT_DISABLE_ERRNO_BACKUP\n\n      Don't save and restore errno\n\n  Values for AARCH64 and ARM only :\n\n  .. cpp:enumerator:: OPT_DISABLE_LOCAL_MONITOR\n\n      Disable the local monitor for instruction like strex\n\n  Values for AARCH64 only :\n\n  .. cpp:enumerator:: OPT_BYPASS_PAUTH\n\n      Disable pointeur authentication\n\n  .. cpp:enumerator:: OPT_ENABLE_BTI\n\n      Enable BTI on instrumented code\n\n  Values for ARM only :\n\n  .. cpp:enumerator:: OPT_DISABLE_D16_D31\n\n      Disable the used of D16-D31 register\n\n  .. cpp:enumerator:: OPT_ARMv4\n\n      Change between ARM and Thumb as an ARMv4 CPU\n\n  .. cpp:enumerator:: OPT_ARMv5T_6\n\n      Change between ARM and Thumb as an ARMv5T or ARMv6 CPU\n\n  .. cpp:enumerator:: OPT_ARMv7\n\n      Change between ARM and Thumb as an ARMv7 CPU (default)\n\n  .. cpp:enumerator:: OPT_ARM_MASK\n\n      When apply :cpp:enumerator:`OPT_ARMv4`,\n      :cpp:enumerator:`OPT_ARMv5T_6` or\n      :cpp:enumerator:`OPT_ARMv7`, this mask must be clear.\n\n  Values for X86 and X86_64 only :\n\n  .. cpp:enumerator:: OPT_ATT_SYNTAX\n\n      Used the AT&T syntax for instruction disassembly\n\n  Values for X86_64 only :\n\n  .. cpp:enumerator:: OPT_ENABLE_FS_GS\n\n      Enable Backup/Restore of FS/GS  segment. This option uses the\n      instructions (RD|WR)(FS|GS)BASE that must be supported by the operating\n      system\n\n.. doxygenenum:: VMError\n    :project: QBDI_C\n\nMiscellaneous\n-------------\n\nVersion\n+++++++\n\n.. doxygenfunction:: qbdi_getVersion\n    :project: QBDI_C\n\nLog\n+++\n\n.. doxygenenum:: LogPriority\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setLogFile\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setLogConsole\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setLogDefault\n    :project: QBDI_C\n\n.. doxygenfunction:: qbdi_setLogPriority\n    :project: QBDI_C\n"
  },
  {
    "path": "docs/source/api_cpp.rst",
    "content": ".. highlight:: c++\n\nC++ API\n=======\n\nIntroduction\n------------\n\nThe C++ API is the primary API of QBDI. The API is compatible with :ref:`QBDIPreload <qbdipreload_api>` on Linux and macOS.\n\n\nVM class\n--------\n\n.. doxygenclass:: QBDI::VM\n    :members: VM, operator=\n\nOptions\n+++++++\n\n.. doxygenfunction:: QBDI::VM::getOptions\n\n.. doxygenfunction:: QBDI::VM::setOptions\n\n.. _state-management-cpp:\n\nState management\n++++++++++++++++\n\n.. doxygenfunction:: QBDI::VM::getGPRState\n\n.. doxygenfunction:: QBDI::VM::getFPRState\n\n.. doxygenfunction:: QBDI::VM::getErrno\n\n.. doxygenfunction:: QBDI::VM::setGPRState\n\n.. doxygenfunction:: QBDI::VM::setFPRState\n\n.. doxygenfunction:: QBDI::VM::setErrno\n\n.. _instrumentation-range-cpp:\n\nInstrumentation range\n+++++++++++++++++++++\n\nAddition\n^^^^^^^^\n\n.. doxygenfunction:: QBDI::VM::addInstrumentedRange\n\n.. doxygenfunction:: QBDI::VM::addInstrumentedModule\n\n.. doxygenfunction:: QBDI::VM::addInstrumentedModuleFromAddr\n\n.. doxygenfunction:: QBDI::VM::instrumentAllExecutableMaps\n\nRemoval\n^^^^^^^\n\n.. doxygenfunction:: QBDI::VM::removeInstrumentedRange\n\n.. doxygenfunction:: QBDI::VM::removeInstrumentedModule\n\n.. doxygenfunction:: QBDI::VM::removeInstrumentedModuleFromAddr\n\n.. doxygenfunction:: QBDI::VM::removeAllInstrumentedRanges\n\nCallback management\n+++++++++++++++++++\n\n.. _instcallback-management-cpp:\n\nInstCallback\n^^^^^^^^^^^^\n\n.. doxygenfunction:: QBDI::VM::addCodeCB(InstPosition pos, InstCallback cbk, void*data, int priority)\n.. doxygenfunction:: QBDI::VM::addCodeCB(InstPosition pos, InstCbLambda &&cbk, int priority)\n.. doxygenfunction:: QBDI::VM::addCodeCB(InstPosition pos, const InstCbLambda &cbk, int priority)\n\n.. doxygenfunction:: QBDI::VM::addCodeAddrCB(rword address, InstPosition pos, InstCallback cbk, void*data, int priority)\n.. doxygenfunction:: QBDI::VM::addCodeAddrCB(rword address, InstPosition pos, InstCbLambda &&cbk, int priority)\n.. doxygenfunction:: QBDI::VM::addCodeAddrCB(rword address, InstPosition pos, const InstCbLambda &cbk, int priority)\n\n.. doxygenfunction:: QBDI::VM::addCodeRangeCB(rword start, rword end, InstPosition pos, InstCallback cbk, void*data, int priority)\n.. doxygenfunction:: QBDI::VM::addCodeRangeCB(rword start, rword end, InstPosition pos, InstCbLambda &&cbk, int priority)\n.. doxygenfunction:: QBDI::VM::addCodeRangeCB(rword start, rword end, InstPosition pos, const InstCbLambda &cbk, int priority)\n\n.. doxygenfunction:: QBDI::VM::addMnemonicCB(const char*mnemonic, InstPosition pos, InstCallback cbk, void*data, int priority)\n.. doxygenfunction:: QBDI::VM::addMnemonicCB(const char*mnemonic, InstPosition pos, InstCbLambda &&cbk, int priority)\n.. doxygenfunction:: QBDI::VM::addMnemonicCB(const char*mnemonic, InstPosition pos, const InstCbLambda &cbk, int priority)\n\n\n.. _vmcallback-management-cpp:\n\nVMEvent\n^^^^^^^\n\n.. doxygenfunction:: QBDI::VM::addVMEventCB(VMEvent mask, VMCallback cbk, void*data)\n.. doxygenfunction:: QBDI::VM::addVMEventCB(VMEvent mask, VMCbLambda &&cbk)\n.. doxygenfunction:: QBDI::VM::addVMEventCB(VMEvent mask, const VMCbLambda &cbk)\n\n.. _memorycallback-management-cpp:\n\nMemoryAccess\n^^^^^^^^^^^^\n\n.. doxygenfunction:: QBDI::VM::addMemAccessCB(MemoryAccessType type, InstCallback cbk, void*data, int priority)\n.. doxygenfunction:: QBDI::VM::addMemAccessCB(MemoryAccessType type, InstCbLambda &&cbk, int priority)\n.. doxygenfunction:: QBDI::VM::addMemAccessCB(MemoryAccessType type, const InstCbLambda &cbk, int priority)\n\n\n.. doxygenfunction:: QBDI::VM::addMemAddrCB(rword address, MemoryAccessType type, InstCallback cbk, void*data)\n.. doxygenfunction:: QBDI::VM::addMemAddrCB(rword address, MemoryAccessType type, InstCbLambda &&cbk)\n.. doxygenfunction:: QBDI::VM::addMemAddrCB(rword address, MemoryAccessType type, const InstCbLambda &cbk)\n\n\n.. doxygenfunction:: QBDI::VM::addMemRangeCB(rword start, rword end, MemoryAccessType type, InstCallback cbk, void*data)\n.. doxygenfunction:: QBDI::VM::addMemRangeCB(rword start, rword end, MemoryAccessType type, InstCbLambda &&cbk)\n.. doxygenfunction:: QBDI::VM::addMemRangeCB(rword start, rword end, MemoryAccessType type, const InstCbLambda &cbk)\n\n\n.. _instrrulecallback-management-cpp:\n\nInstrRuleCallback\n^^^^^^^^^^^^^^^^^\n\n.. doxygenfunction:: QBDI::VM::addInstrRule(InstrRuleCallback cbk, AnalysisType type, void* data)\n.. doxygenfunction:: QBDI::VM::addInstrRule(InstrRuleCbLambda &&cbk, AnalysisType type)\n.. doxygenfunction:: QBDI::VM::addInstrRule(const InstrRuleCbLambda &cbk, AnalysisType type)\n\n.. doxygenfunction:: QBDI::VM::addInstrRuleRange(rword start, rword end, InstrRuleCallback cbk, AnalysisType type, void* data)\n.. doxygenfunction:: QBDI::VM::addInstrRuleRange(rword start, rword end, InstrRuleCbLambda &&cbk, AnalysisType type)\n.. doxygenfunction:: QBDI::VM::addInstrRuleRange(rword start, rword end, const InstrRuleCbLambda &cbk, AnalysisType type)\n\n.. doxygenfunction:: QBDI::VM::addInstrRuleRangeSet(RangeSet<rword> range, InstrRuleCallback cbk, AnalysisType type, void*data)\n.. doxygenfunction:: QBDI::VM::addInstrRuleRangeSet(RangeSet<rword> range, InstrRuleCbLambda &&cbk, AnalysisType type)\n.. doxygenfunction:: QBDI::VM::addInstrRuleRangeSet(RangeSet<rword> range, const InstrRuleCbLambda &cbk, AnalysisType type)\n\n\nRemoval\n^^^^^^^\n\n.. doxygenfunction:: QBDI::VM::deleteInstrumentation\n\n.. doxygenfunction:: QBDI::VM::deleteAllInstrumentations\n\nRun\n+++\n\n.. doxygenfunction:: QBDI::VM::run\n\n.. doxygenfunction:: QBDI::VM::call\n\n.. doxygenfunction:: QBDI::VM::callA\n\n.. doxygenfunction:: QBDI::VM::callV\n\n.. doxygenfunction:: QBDI::VM::switchStackAndCall\n\n.. doxygenfunction:: QBDI::VM::switchStackAndCallA\n\n.. doxygenfunction:: QBDI::VM::switchStackAndCallV\n\n.. _instanalysis-getter-cpp:\n\nInstAnalysis\n++++++++++++\n\n.. doxygenfunction:: QBDI::VM::getInstAnalysis\n\n.. doxygenfunction:: QBDI::VM::getCachedInstAnalysis\n\n.. doxygenfunction:: QBDI::VM::getJITInstAnalysis\n\n.. _memaccess-getter-cpp:\n\nMemoryAccess\n++++++++++++\n\n.. doxygenfunction:: QBDI::VM::getInstMemoryAccess\n\n.. doxygenfunction:: QBDI::VM::getBBMemoryAccess\n\n.. doxygenfunction:: QBDI::VM::recordMemoryAccess\n\nCache management\n++++++++++++++++\n\n.. doxygenfunction:: QBDI::VM::precacheBasicBlock\n\n.. doxygenfunction:: QBDI::VM::clearCache\n\n.. doxygenfunction:: QBDI::VM::clearAllCache\n\n.. doxygenfunction:: QBDI::VM::getNbExecBlock\n\n.. doxygenfunction:: QBDI::VM::reduceCacheTo\n\n.. _register-state-cpp:\n\nRegister state\n--------------\n\n.. cpp:type:: QBDI::rword\n\n    An integer of the size of a register\n\n    - uint32_t for X86 and ARM\n    - uint64_t for X86_64 and AARCH64\n\n.. cpp:struct:: QBDI::GPRState\n\n    General Purpose Register context.\n\n    For X86 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86/State.h\n       :start-after: SPHINX_X86_GPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_GPRSTATE_END\n       :code:\n\n    For X86_64 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86_64/State.h\n       :start-after: SPHINX_X86_64_GPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_64_GPRSTATE_END\n       :code:\n\n    For ARM architecture:\n\n    .. include:: ../../include/QBDI/arch/ARM/State.h\n       :start-after: SPHINX_ARM_GPRSTATE_BEGIN\n       :end-before: // SPHINX_ARM_GPRSTATE_END\n       :code:\n\n    For AARCH64 architecture:\n\n    .. include:: ../../include/QBDI/arch/AARCH64/State.h\n       :start-after: SPHINX_AARCH64_GPRSTATE_BEGIN\n       :end-before: // SPHINX_AARCH64_GPRSTATE_END\n       :code:\n\n.. cpp:struct:: QBDI::FPRState\n\n    Floating Point Register context.\n\n    For X86 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86/State.h\n       :start-after: SPHINX_X86_FPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_FPRSTATE_END\n       :code:\n\n    For X86_64 architecture:\n\n    .. include:: ../../include/QBDI/arch/X86_64/State.h\n       :start-after: SPHINX_X86_64_FPRSTATE_BEGIN\n       :end-before: // SPHINX_X86_64_FPRSTATE_END\n       :code:\n\n    For ARM architecture:\n\n    .. include:: ../../include/QBDI/arch/ARM/State.h\n       :start-after: SPHINX_ARM_FPRSTATE_BEGIN\n       :end-before: // SPHINX_ARM_FPRSTATE_END\n       :code:\n\n    For AARCH64 architecture:\n\n    .. include:: ../../include/QBDI/arch/AARCH64/State.h\n       :start-after: SPHINX_AARCH64_FPRSTATE_BEGIN\n       :end-before: // SPHINX_AARCH64_FPRSTATE_END\n       :code:\n\n.. doxygenstruct:: QBDI::MMSTReg\n    :members:\n    :undoc-members:\n\n.. doxygenstruct:: QBDI::FPControl\n    :members:\n    :undoc-members:\n\n.. doxygenstruct:: QBDI::FPStatus\n    :members:\n    :undoc-members:\n\n.. data:: QBDI::REG_RETURN\n\n.. data:: QBDI::REG_BP\n\n.. data:: QBDI::REG_SP\n\n.. data:: QBDI::REG_PC\n\n.. data:: QBDI::NUM_GPR\n\n.. _callback-cpp:\n\nCallback\n--------\n\n.. doxygentypedef:: QBDI::VMInstanceRef\n\n.. doxygentypedef:: QBDI::InstCallback\n\n.. doxygentypedef:: QBDI::InstCbLambda\n\n.. doxygentypedef:: QBDI::VMCallback\n\n.. doxygentypedef:: QBDI::VMCbLambda\n\n.. doxygentypedef:: QBDI::InstrRuleCallback\n\n.. doxygentypedef:: QBDI::InstrRuleCbLambda\n\n.. doxygenstruct:: QBDI::InstrRuleDataCBK\n    :members:\n\n.. doxygenenum:: QBDI::InstPosition\n\n.. doxygenenum:: QBDI::CallbackPriority\n\n.. doxygenenum:: QBDI::VMAction\n\n.. _instanalysis-cpp:\n\nInstAnalysis\n------------\n\n.. doxygenenum:: QBDI::AnalysisType\n\n.. doxygenstruct:: QBDI::InstAnalysis\n    :members:\n\n.. doxygenenum:: QBDI::ConditionType\n\n.. doxygenstruct:: QBDI::OperandAnalysis\n    :members:\n\n.. doxygenenum:: QBDI::OperandType\n\n.. doxygenenum:: QBDI::OperandFlag\n\n.. doxygenenum:: QBDI::RegisterAccessType\n\n.. _memaccess-cpp:\n\nMemoryAccess\n------------\n\n.. doxygenstruct:: QBDI::MemoryAccess\n    :members:\n\n.. doxygenenum:: QBDI::MemoryAccessType\n\n.. doxygenenum:: QBDI::MemoryAccessFlags\n\n.. _vmevent-cpp:\n\nVMEvent\n-------\n\n.. doxygenenum:: QBDI::VMEvent\n\n.. doxygenstruct:: QBDI::VMState\n    :members:\n\nMemory management\n-----------------\n\nAllocation\n++++++++++\n\n.. doxygenfunction:: QBDI::alignedAlloc\n\n.. doxygenfunction:: QBDI::allocateVirtualStack\n\n.. doxygenfunction:: QBDI::alignedFree\n\n.. doxygenfunction:: QBDI::simulateCall\n\n.. doxygenfunction:: QBDI::simulateCallV\n\n.. doxygenfunction:: QBDI::simulateCallA\n\nExploration\n+++++++++++\n\n.. doxygenfunction:: QBDI::getModuleNames\n\n.. doxygenfunction:: QBDI::getCurrentProcessMaps\n\n.. doxygenfunction:: QBDI::getRemoteProcessMaps\n\n.. doxygenstruct:: QBDI::MemoryMap\n    :members: range, permission, name\n\n.. doxygenenum:: QBDI::Permission\n\nOther globals\n-------------\n\n.. cpp:enum:: QBDI::Options\n\n  Note: some value are available only for some architecture\n\n  Values for all architecture :\n\n  .. cpp:enumerator:: NO_OPT\n\n      Default Value\n\n  .. cpp:enumerator:: OPT_DISABLE_FPR\n\n      Disable all operation on FPU (SSE, AVX, SIMD). May break the execution if the target use the FPU\n\n  .. cpp:enumerator:: OPT_DISABLE_OPTIONAL_FPR\n\n      Disable context switch optimisation when the target execblock doesn't used FPR\n\n  .. cpp:enumerator:: OPT_DISABLE_MEMORYACCESS_VALUE\n\n      Don't load memory access value\n\n  .. cpp:enumerator:: OPT_DISABLE_ERRNO_BACKUP\n\n      Don't save and restore errno\n\n  Values for AARCH64 and ARM only :\n\n  .. cpp:enumerator:: OPT_DISABLE_LOCAL_MONITOR\n\n      Disable the local monitor for instruction like strex\n\n  Values for AARCH64 only :\n\n  .. cpp:enumerator:: OPT_BYPASS_PAUTH\n\n      Disable pointeur authentication\n\n  .. cpp:enumerator:: OPT_ENABLE_BTI\n\n      Enable BTI on instrumented code\n\n  Values for ARM only :\n\n  .. cpp:enumerator:: OPT_DISABLE_D16_D31\n\n      Disable the used of D16-D31 register\n\n  .. cpp:enumerator:: OPT_ARMv4\n\n      Change between ARM and Thumb as an ARMv4 CPU\n\n  .. cpp:enumerator:: OPT_ARMv5T_6\n\n      Change between ARM and Thumb as an ARMv5T or ARMv6 CPU\n\n  .. cpp:enumerator:: OPT_ARMv7\n\n      Change between ARM and Thumb as an ARMv7 CPU (default)\n\n  .. cpp:enumerator:: OPT_ARM_MASK\n\n      When apply :cpp:enumerator:`OPT_ARMv4`,\n      :cpp:enumerator:`OPT_ARMv5T_6` or\n      :cpp:enumerator:`OPT_ARMv7`, this mask must be clear.\n\n  Values for X86 and X86_64 only :\n\n  .. cpp:enumerator:: OPT_ATT_SYNTAX\n\n      Used the AT&T syntax for instruction disassembly\n\n  Values for X86_64 only :\n\n  .. cpp:enumerator:: OPT_ENABLE_FS_GS\n\n      Enable Backup/Restore of FS/GS  segment. This option uses the\n      instructions (RD|WR)(FS|GS)BASE that must be supported by the operating\n      system\n\n\n\n.. doxygenenum:: QBDI::VMError\n\nMiscellaneous\n-------------\n\nVersion\n+++++++\n\n.. doxygenfunction:: QBDI::getVersion\n\nLog\n+++\n\n.. doxygenenum:: QBDI::LogPriority\n\n.. doxygenfunction:: QBDI::setLogFile\n\n.. doxygenfunction:: QBDI::setLogPriority\n\n.. doxygenfunction:: QBDI::setLogConsole\n\n.. doxygenfunction:: QBDI::setLogDefault\n\nRange\n+++++\n\n.. doxygenclass:: QBDI::Range\n    :members:\n    :undoc-members:\n\n.. doxygenclass:: QBDI::RangeSet\n    :members:\n    :undoc-members:\n\n"
  },
  {
    "path": "docs/source/api_description.rst",
    "content": "API description\n===============\n\nThis section gives the big picture of all the APIs QBDI offers.\nIt may help you better understand the way QBDI works and the overall design behind it.\n\nInjection\n---------\n\nInstrumentation with QBDI implies having both QBDI and the target program inside the same process.\nThree types of injections are currently possible with QBDI.\n\nCreate a QBDI program\n+++++++++++++++++++++\n\nThe first type of injection is to create an executable that uses QBDI and loads the target code.\nThis injection is available with C, C++ and PyQBDI APIs and should be used if you can compile the\ntarget code inside the client binary or load it as a library.\n\nThe main steps of this injection are:\n\n- Load the code (``dlopen``, ``LoadLibrary``, ...)\n- Initialise a new QBDI VM\n- Select instrumentation ranges\n- Allocate a stack for the target process\n- Define callbacks\n- Call a target method inside the VM\n\nInject a preload library\n++++++++++++++++++++++++\n\nThis injection forces the loader to add a custom library inside a new process.\nThe preloaded library will catch the main address and allow users to instrument it.\nThis injection should be used if you want to instrument a program from the start\nof the ``main`` method to the end.\n\nWe provide a small tool called :ref:`QBDIPreload <get_started-qbdipreload>` for doing so on Linux and macOS.\nQBDIPreload (and its implementation :ref:`PyQBDIPreload <get_started-pyqbdipreload>` for :ref:`PyQBDI <get_started-pyqbdi>`)\nautomatically puts a hook on the ``main`` method and accordingly prepares a QBDI VM that can be used out of the box.\n\nTypically, the different stages are:\n\n- Wait for :cpp:func:`qbdipreload_on_main` or :py:func:`pyqbdipreload_on_run` to be called\n  with an initialised QBDI VM\n- Define callbacks\n- Run the ``main`` method inside the VM\n\nInject with Frida\n+++++++++++++++++\n\nFrida works on a wide variety of systems and brings powerful and handy injection mechanisms.\nIt is why the Frida/QBDI subproject relies on those to inject itself into processes.\nOn top of that, while developing their scripts, users are free to take advantage of both Frida and QBDI APIs.\n\nTracing a specific function can be summed up as:\n\n- Inject a script into a process with Frida\n- Put callbacks on the target functions\n- Wait for the hooks to be triggered\n- Create a QBDI VM, synchronise the context and define instrumentation ranges\n- Register callbacks\n- Remove the instrumentation added by Frida\n- Run the function in the context of QBDI\n- Restore the Frida hook for potential future calls\n- Return the return value obtained from QBDI\n\n.. _instrumentation_range:\n\nInstrumentation ranges\n----------------------\n- Global APIs: :ref:`C <instrumentation-range-c>`, :ref:`C++ <instrumentation-range-cpp>`, :ref:`PyQBDI <instrumentation-range-pyqbdi>`, :ref:`Frida/QBDI <instrumentation-range-js>`\n\nThe Instrumentation Range is a range of addresses where QBDI will instrument the original code.\nWhen the programme counter gets out of this scope, QBDI will try to restore a native and uninstrumented execution.\nFor performance reasons, it is rarely recommended to instrument all the guest code.\nMoreover, you must bear in mind that QBDI shares the same standard library as the guest and some methods are not *reentrant* (more details in :ref:`intro_limitations`).\n\nThe current mechanism is implemented by the :cpp:class:`ExecBroker` and only supports external calls out of the instrumentation range.\nWhen the execution gets out of the range, :cpp:class:`ExecBroker` will try to find an address on the stack that is inside the instrumentation range.\nIf an address is found, it will be replaced by a custom one and the execution is restored without instrumentation. When the process returns to\nthis address :cpp:class:`ExecBroker` will capture its state and continue the execution at the expected address. If no valid return address is found,\nthe instrumentation will continue until finding a valid return address.\n\nThe following limitations are known:\n\n- The instrumentation range must be at a function level, and if possible, at library level.\n  A range that includes only some instructions of a function will produce an unpredictable result.\n- When the native instrumentation goes out of the instrumentation range, the only method to restore\n  the instrumentation is to return to the modified address. Any other executions of code inside the\n  instrumentation range will not be caught (callbacks, ...).\n- The current :cpp:class:`ExecBroker` doesn't support any exception mechanism, including the `setjmp/longjmp`.\n- The instrumentation range, and QBDI in general, are **not** a security sandbox. The code may\n  escape and run without instrumentation.\n\nThe instrumentation ranges can be managed through:\n\n- ``addInstrumentedRange`` and ``removeInstrumentedRange`` to add or remove a specific range of addresses\n- ``addInstrumentedModule`` and ``removeInstrumentedModule`` to add or remove a library/module with its name\n- ``addInstrumentedModuleFromAddr`` and ``removeInstrumentedModuleFromAddr`` to add or remove a library/module with one of its addresses\n- ``instrumentAllExecutableMaps`` and ``removeAllInstrumentedRanges`` to add or remove all the executable ranges\n\n\nRegister state\n--------------\n- Global APIs: :ref:`C <register-state-c>`, :ref:`C++ <register-state-cpp>`, :ref:`PyQBDI <register-state-pyqbdi>`, :ref:`Frida/QBDI <register-state-js>`\n- Management APIs: :ref:`C <state-management-c>`, :ref:`C++ <state-management-cpp>`, :ref:`PyQBDI <state-management-pyqbdi>`, :ref:`Frida/QBDI <state-management-js>`\n\nQBDI defines two structures for the registers: ``GPRState`` and ``FPRState``.\n\n- ``GPRState`` contains all the General Purpose registers such as ``rax``, ``rsp``, ``rip`` or ``eflags`` on X86_64.\n- ``FPRState`` contains the Floating Point registers.\n\nInside a ``InstCallback`` and ``VMCallback``, the current state is passed as a parameter and any change on it will affect the execution.\nOutside of a callback, ``GPRState`` and ``FPRState`` can be retrieved and set with ``getGPRState``, ``getFPRState``, ``setGPRState`` and ``setFPRState``.\n\n.. note::\n\n    A modification of the instruction counter (e.g. ``RIP``) in an ``InstCallback`` or a ``VMCallback`` is not effective if ``BREAK_TO_VM`` is not returned.\n\nUser callbacks\n--------------\n- Global APIs: :ref:`C <callback-c>`, :ref:`C++ <callback-cpp>`, :ref:`PyQBDI <callback-pyqbdi>`, :ref:`Frida/QBDI <callback-js>`\n\nQBDI allows users to register callbacks that are called throughout the execution of the target. These callbacks can be used to determine what the program is doing\nor to modify its state. Some callbacks must return an action to specify whether the execution should continue or stop or if the context needs to be reevaluated.\n\nAll user callbacks must be written in C, C++, Python or JS. However, there are a few limitations:\n\n- As the target registers are saved, the callback can use any register by respecting the standard calling convention of the current platform.\n- Some methods of the VM are not *reentrant* and must not be called within the scope of a callback.\n  (``run``, ``call``, ``setOptions``, ``precacheBasicBlock``, destructor, copy and move operators)\n- The ``BREAK_TO_VM`` action should be returned instead of the ``CONTINUE`` action if the state of the VM is somehow changed. It covers:\n\n  - Add or remove callbacks\n  - Modify instrumentation ranges\n  - Clear the cache\n  - Change the instruction counter register in ``GPRState`` (the other registers can be altered without the need of returning ``BREAK_TO_VM``).\n\n.. _api_desc_InstCallback:\n\nInstruction callbacks\n+++++++++++++++++++++\n- Global APIs: :ref:`C <instcallback-management-c>`, :ref:`C++ <instcallback-management-cpp>`, :ref:`PyQBDI <instcallback-management-pyqbdi>`, :ref:`Frida/QBDI <instcallback-management-js>`\n\nAn instruction callback (``InstCallback``) is a callback that will be called **before** or **after** executing an instruction.\nTherefore, an ``InstCallback`` can be inserted at two different positions:\n\n- before the instruction (``PREINST`` -- short for *pre-instruction*)\n- after the instruction (``POSTINST`` -- short for *post-instruction*). At this point, the register state has been automatically updated so the instruction counter points to the next code address.\n\n.. note::\n\n    A ``POSTINST`` callback will be called after the instruction and before the next one. If on a call instruction, the callback\n    is then called before the first instruction of the called method.\n\nAn ``InstCallback`` can be registered for a specific instruction (``addCodeAddrCB``),\nany instruction in a specified range (``addCodeRangeCB``) or any instrumented instruction (``addCodeCB``).\nThe instruction also be targeted by its mnemonic (or LLVM opcode) (``addMnemonicCB``).\n\n.. _api_desc_VMCallback:\n\nVM callbacks\n++++++++++++\n- ``VMCallback`` APIs: :ref:`C <vmcallback-management-c>`, :ref:`C++ <vmcallback-management-cpp>`, :ref:`PyQBDI <vmcallback-management-pyqbdi>`, :ref:`Frida/QBDI <vmcallback-management-js>`\n- ``VMEvent`` APIs: :ref:`C <vmevent-c>`, :ref:`C++ <vmevent-cpp>`, :ref:`PyQBDI <vmevent-pyqbdi>`, :ref:`Frida/QBDI <vmevent-js>`\n\nA ``VMEvent`` callback (``VMCallback``) is a callback that will be called when the VM reaches a specific state. The current supported events are:\n\n- At the beginning and the end of a basic block (``BASIC_BLOCK_ENTRY`` and ``BASIC_BLOCK_EXIT``).\n  A basic block in QBDI consists of consecutive instructions that don't change the instruction counter except the last one.\n  These events are triggered respectively by ``SEQUENCE_ENTRY`` and ``SEQUENCE_EXIT`` as well.\n- At the beginning and the end of a sequence (``SEQUENCE_ENTRY`` and ``SEQUENCE_EXIT``).\n  A sequence is a part of a basic block that has been *JIT'd* consecutively. These events should only be used for ``getBBMemoryAccess``.\n- When a new uncached basic block is being *JIT'd* (``BASIC_BLOCK_NEW``). This event is also always triggered by ``BASIC_BLOCK_ENTRY`` and ``SEQUENCE_ENTRY``.\n- Before and after executing some uninstrumented code with the :cpp:class:`ExecBroker` (``EXEC_TRANSFER_CALL`` and ``EXEC_TRANSFER_RETURN``).\n\nWhen a ``VMCallback`` is called, a state of the VM (``VMState``) is passed in argument. This state contains:\n\n- A set of events that trigger the callback. If the callback is registered for several events that trigger at the same moment,\n  the callback will be called only once.\n- If the event is related to a basic block or a sequence, the start and the end addresses of the current basic block and sequence are provided.\n\nMemory callbacks\n++++++++++++++++\n- Global APIs: :ref:`C <memorycallback-management-c>`, :ref:`C++ <memorycallback-management-cpp>`, :ref:`PyQBDI <memorycallback-management-pyqbdi>`, :ref:`Frida/QBDI <memorycallback-management-js>`\n\nThe memory callback is an ``InstCallback`` that will be called when the target program reads or writes the memory.\nThe callback can be called only when a specific address is accessed (``addMemAddrCB``),\nwhen a range of addresses is accessed (``addMemRangeCB``) or when any memory is accessed (``addMemAccessCB``).\n\nUnlike with the instruction callback registration, the position of a memory callback cannot be manually specified.\nIf a memory callback is solely registered for read accesses, it will be called **before** the instruction.\nOtherwise, it will be called **after** executing the instruction.\n\nInstrumentation rule callbacks\n++++++++++++++++++++++++++++++\n- Global APIs: :ref:`C <instrrulecallback-management-c>`, :ref:`C++ <instrrulecallback-management-cpp>`, :ref:`PyQBDI <instrrulecallback-management-pyqbdi>`, :ref:`Frida/QBDI <instrrulecallback-management-js>`\n\nInstrumentation rule callbacks are an advanced feature of QBDI. It allows users to define a callback (``InstrRuleCallback``) that will be called during the instrumentation process.\nThe callback will be called for each instruction and can define an ``InstCallback`` to call before or after the current instruction.\nAn argument contains an ``InstAnalysis`` of the current instruction and can be used to define the callback to insert for this instruction.\n\nAn ``InstrRuleCallback`` can be registered for all instructions (``addInstrRule``) or only for a specific range (``addInstrRuleRange``).\n\n.. note::\n\n    The instrumentation process of QBDI responsible for *JITing* instructions may analyse more than once the same instruction.\n    Consequently, the instrumentation rule callback must always return the same result even though the instruction has already been instrumented.\n\nInstruction analysis\n--------------------\n- Global APIs: :ref:`C <instanalysis-c>`, :ref:`C++ <instanalysis-cpp>`, :ref:`PyQBDI <instanalysis-pyqbdi>`, :ref:`Frida/QBDI <instanalysis-js>`\n- Getter APIs: :ref:`C <instanalysis-getter-c>`, :ref:`C++ <instanalysis-getter-cpp>`, :ref:`PyQBDI <instanalysis-getter-pyqbdi>`, :ref:`Frida/QBDI <instanalysis-getter-js>`\n\nQBDI provides some basic analysis of the instruction through the ``InstAnalysis`` object. Within an ``InstCallback``, the analysis of the current instruction should be retrieved with\n``getInstAnalysis``. Otherwise, the analysis of any instruction in the cache can be obtained with ``getCachedInstAnalysis``. The ``InstAnalysis`` is cached inside QBDI and\nis valid until the next cache modification (add new instructions, clear cache, ...).\n\nFour types of analysis are available. If a type of analysis is not selected, the corresponding field of the ``InstAnalysis`` object remains empty and should not be used.\n\n- ``ANALYSIS_INSTRUCTION``: This analysis type provides some generic information about the instruction, like its address, its size, its mnemonic (LLVM opcode)\n  or its condition type if the instruction is conditional.\n- ``ANALYSIS_DISASSEMBLY``: This analysis type provides the disassembly of the instruction. For X86 and X86_64, the Intel syntax is used by default.\n  The syntax can be changed with the option ``OPT_ATT_SYNTAX``.\n- ``ANALYSIS_OPERANDS``: This analysis type provides information about the operands of the instruction.\n  An operand can be a register or an immediate. If a register operand can be empty, the special type ``OPERAND_INVALID`` is used.\n  The implicit register of instruction is also present with a specific flag.\n  Moreover, the member ``flagsAccess`` specifies whether the instruction will use or set the generic flag.\n- ``ANALYSIS_SYMBOL``: This analysis type detects whether a symbol is associated with the current instruction.\n\nThe source file ``test/API/InstAnalysisTest_<arch>.cpp`` shows how one can deal with instruction analysis and may be taken as a reference for the ``ANALYSIS_INSTRUCTION`` and ``ANALYSIS_OPERANDS`` types.\n\nMemory accesses\n---------------\n- Global APIs: :ref:`C <memaccess-c>`, :ref:`C++ <memaccess-cpp>`, :ref:`PyQBDI <memaccess-pyqbdi>`, :ref:`Frida/QBDI <memaccess-js>`\n- Getter APIs: :ref:`C <memaccess-getter-c>`, :ref:`C++ <memaccess-getter-cpp>`, :ref:`PyQBDI <memaccess-getter-pyqbdi>`, :ref:`Frida/QBDI <memaccess-getter-js>`\n\nDue to performance considerations, the capture of memory accesses (``MemoryAccess``) is not enabled by default.\nIt is only turned on when a memory callback is registered or explicitly requested with ``recordMemoryAccess``.\nCollecting read and written accesses can be enabled either together or separately.\n\nTwo APIs can be used to get the memory accesses:\n\n- ``getInstMemoryAccess`` can be used within an instruction callback or a memory callback to retrieve the access of the current instruction.\n  If the callback is before the instruction (``PREINST``), only read accesses will be available.\n- ``getBBMemoryAccess`` must be used in a ``VMEvent`` callback with ``SEQUENCE_EXIT`` to get all the memory accesses for the last sequence.\n\nBoth return a list of ``MemoryAccess``. Generally speaking, a ``MemoryAccess`` will have the address of the instruction responsible for the access,\nthe access address and size, the type of access and the value read or written. However, some instructions can do complex accesses and\nsome information can be missing or incomplete. The ``flags`` of ``MemoryAccess`` can be used to detect these cases:\n\n- ``MEMORY_UNKNOWN_SIZE``: the size of the access is unknown.\n  This is currently used for instruction with ``REP`` prefix before the execution of the instruction.\n  The size is determined after the instruction when the access has been completed.\n- ``MEMORY_MINIMUM_SIZE``: The size of the access is a minimum size. The access is complex but at least ``size`` of memory is accessed.\n  This is currently used for the ``XSAVE*`` and ``XRSTOR*`` instructions.\n- ``MEMORY_UNKNOWN_VALUE``: The value of the access hasn't been captured. This flag will be used when the access size is greater than the size of a ``rword``.\n  It's also used for instructions with ``REP`` in ``X86`` and ``X86_64``.\n\n\nOptions\n-------\n\nThe ``options`` of the VM allow changing some internal mechanisms of QBDI. For most uses of QBDI, no option needs to be specified.\nThe options are specified when the VM is created and can be changed with ``setOptions`` when the VM is not running. After changing the options,\nthe cache needs to be cleared to apply the changes to all the instructions.\n\n- ``OPT_DISABLE_FPR``: This option disables the Floating Point Registers support. QBDI will not back up and restore any ``FPRState`` registers.\n- ``OPT_DISABLE_OPTIONAL_FPR``: if ``OPT_DISABLE_FPR`` is not enabled, this option will force the ``FPRState`` to be restored and saved\n  before and after any instruction. By default, QBDI will try to detect the instructions that make use of floating point registers and only restore for\n  these precise instructions.\n- ``OPT_ATT_SYNTAX``: For X86 and X86_64 architectures, this option changes\n  the syntax of ``InstAnalysis.disassembly`` to AT&T instead of the Intel one.\n"
  },
  {
    "path": "docs/source/api_js.rst",
    "content": ".. _frida-qbdi-api:\n\nFrida/QBDI API\n==============\n\nIntroduction\n------------\n\nWe provide bindings for Frida, most C/C++ APIs are exported and available through them, but they are also backed up by plenty of helpers that fluidify the script writing.\n\nNevertheless, this API slightly differs from the C++ API:\n\n- Every callback must be created as a native function.\n  The callback registration can be done through calling :js:func:`VM.newInstCallback`, :js:func:`VM.newVMCallback` and :js:func:`VM.newInstrRuleCallback`.\n- The *QBDI* class is the equivalent of the *VM* class we have in the C++ API.\n\n\nQBDI class\n----------\n\nWith Frida API, the QBDI object is the equivalent of the VM object in C++ API.\n\n.. js:autoclass:: VM\n   :members:\n   :exclude-members: newInstrRuleCallback, newInstCallback, newVMCallback, addMnemonicCB,\n                     addCodeCB, addCodeAddrCB, addCodeRangeCB, addVMEventCB, addMemAccessCB, addMemAddrCB, addMemRangeCB,\n                     recordMemoryAccess, addInstrRule, addInstrRuleRange, deleteAllInstrumentations, deleteInstrumentation,\n                     addInstrumentedModule, addInstrumentedModuleFromAddr, addInstrumentedRange, instrumentAllExecutableMaps,\n                     removeInstrumentedRange, removeInstrumentedModule, removeInstrumentedModuleFromAddr, removeAllInstrumentedRanges,\n                     getInstAnalysis, getCachedInstAnalysis, getInstMemoryAccess, getBBMemoryAccess, precacheBasicBlock,\n                     clearCache, clearAllCache, getGPRState, getFPRState, getErrno, setGPRState, setFPRState, setErrno, run, call, simulateCall,\n                     allocateVirtualStack, alignedAlloc, alignedFree, getModuleNames, getOptions, setOptions\n\nOptions\n+++++++\n\n.. js:autofunction:: VM#getOptions\n\n.. js:autofunction:: VM#setOptions\n\n.. _state-management-js:\n\nState management\n++++++++++++++++\n\n.. js:autofunction:: VM#getGPRState\n\n.. js:autofunction:: VM#getFPRState\n\n.. js:autofunction:: VM#getErrno\n\n.. js:autofunction:: VM#setGPRState\n\n.. js:autofunction:: VM#setFPRState\n\n.. js:autofunction:: VM#setErrno\n\n.. _instrumentation-range-js:\n\nInstrumentation range\n+++++++++++++++++++++\n\nAddition\n^^^^^^^^\n\n.. js:autofunction:: VM#addInstrumentedRange\n\n.. js:autofunction:: VM#addInstrumentedModule\n\n.. js:autofunction:: VM#addInstrumentedModuleFromAddr\n\n.. js:autofunction:: VM#instrumentAllExecutableMaps\n\nRemoval\n^^^^^^^\n\n.. js:autofunction:: VM#removeInstrumentedRange\n\n.. js:autofunction:: VM#removeInstrumentedModule\n\n.. js:autofunction:: VM#removeInstrumentedModuleFromAddr\n\n.. js:autofunction:: VM#removeAllInstrumentedRanges\n\nCallback management\n+++++++++++++++++++\n\nCreation\n^^^^^^^^\n\n.. js:autofunction:: VM#newInstCallback\n\n.. js:autofunction:: VM#newInstrRuleCallback\n\n.. js:autofunction:: VM#newVMCallback\n\n.. _instcallback-management-js:\n\nInstCallback\n^^^^^^^^^^^^\n\n.. js:autofunction:: VM#addCodeCB\n\n.. js:autofunction:: VM#addCodeAddrCB\n\n.. js:autofunction:: VM#addCodeRangeCB\n\n.. js:autofunction:: VM#addMnemonicCB\n\n.. _vmcallback-management-js:\n\nVMEvent\n^^^^^^^\n\n.. js:autofunction:: VM#addVMEventCB\n\n.. _memorycallback-management-js:\n\nMemoryAccess\n^^^^^^^^^^^^\n\n.. js:autofunction:: VM#addMemAccessCB\n\n.. js:autofunction:: VM#addMemAddrCB\n\n.. js:autofunction:: VM#addMemRangeCB\n\n.. _instrrulecallback-management-js:\n\nInstrRuleCallback\n^^^^^^^^^^^^^^^^^\n\n.. js:autofunction:: VM#addInstrRule\n\n.. js:autofunction:: VM#addInstrRuleRange\n\nRemoval\n^^^^^^^\n\n.. js:autofunction:: VM#deleteInstrumentation\n\n.. js:autofunction:: VM#deleteAllInstrumentations\n\nMemory management\n+++++++++++++++++\n\nAllocation\n^^^^^^^^^^\n\n.. js:autofunction:: VM#alignedAlloc\n\n.. js:autofunction:: VM#allocateVirtualStack\n\n.. js:autofunction:: VM#alignedFree\n\n\nExploration\n^^^^^^^^^^^\n\n.. js:autofunction:: VM#getModuleNames\n\nRun\n+++\n\n.. js:autofunction:: VM#run\n\n.. js:autofunction:: VM#call\n\n.. js:autofunction:: VM#switchStackAndCall\n\n.. js:autofunction:: VM#simulateCall\n\n.. _instanalysis-getter-js:\n\nInstAnalysis\n++++++++++++\n\n.. js:autofunction:: VM#getInstAnalysis\n\n.. js:autofunction:: VM#getCachedInstAnalysis\n\n.. js:autofunction:: VM#getJITInstAnalysis\n\n.. _memaccess-getter-js:\n\nMemoryAccess\n++++++++++++\n\n.. js:autofunction:: VM#getInstMemoryAccess\n\n.. js:autofunction:: VM#getBBMemoryAccess\n\n.. js:autofunction:: VM#recordMemoryAccess\n\nCache management\n++++++++++++++++\n\n.. js:autofunction:: VM#precacheBasicBlock\n\n.. js:autofunction:: VM#clearCache\n\n.. js:autofunction:: VM#clearAllCache\n\n.. js:autofunction:: VM#getNbExecBlock\n\n.. js:autofunction:: VM#reduceCacheTo\n\n.. _register-state-js:\n\nRegister state\n--------------\n\n.. js:autoclass:: GPRState\n   :members:\n\n.. js:autoclass:: SyncDirection\n\n    .. js:autoattribute:: QBDI_TO_FRIDA\n    .. js:autoattribute:: FRIDA_TO_QBDI\n\n.. js:autoattribute:: GPR_NAMES\n\n.. js:autoattribute:: REG_PC\n\n.. js:autoattribute:: REG_RETURN\n\n.. js:autoattribute:: REG_SP\n\n.. _callback-js:\n\nCallback\n--------\n\n.. js:function:: InstCallback(vm, gpr, fpr, data)\n\n    This is the prototype of a function callback for:\n\n    - :js:func:`VM.addCodeCB`, :js:func:`VM.addCodeAddrCB` and :js:func:`VM.addCodeRangeCB`\n    - :js:func:`VM.addMnemonicCB`\n    - :js:func:`VM.addMemAccessCB`, :js:func:`VM.addMemAddrCB` and :js:func:`VM.addMemRangeCB`\n    - :js:class:`InstrRuleDataCBK`.\n\n    The function must be registered with :js:func:`VM.newInstCallback`.\n\n    :param QBDI     vm:    The current QBDI object\n    :param GPRState gpr:   The current GPRState\n    :param FPRState fpr:   The current FPRState\n    :param Object   data:  A user-defined object.\n\n    :return: the :js:class:`VMAction` to continue or stop the execution\n\n.. js:function:: VMCallback(vm, vmState, gpr, fpr, data)\n\n    This is the prototype of a function callback for :js:func:`VM.addVMEventCB`.\n    The function must be registered with :js:func:`VM.newVMCallback`.\n\n    :param QBDI     vm:      The current QBDI object\n    :param VMState  vmState: A structure containing the current state of the VM.\n    :param GPRState gpr:     The current GPRState\n    :param FPRState fpr:     The current FPRState\n    :param Object   data:    A user-defined object\n\n    :return: the :js:class:`VMAction` to continue or stop the execution\n\n.. js:function:: InstrRuleCallback(vm, ana, data)\n\n    This is the prototype of a function callback for :js:func:`VM.addInstrRule` and :js:func:`VM.addInstrRuleRange`.\n    The function must be registered with :js:func:`VM.newInstrRuleCallback`.\n\n    :param QBDI         vm:   The current QBDI object\n    :param InstAnalysis ana:  The current QBDI object\n    :param Object       data: A user-defined object\n\n    :return: An Array of :js:class:`InstrRuleDataCBK`\n\n.. js:autoclass:: InstrRuleDataCBK\n\n.. js:autoclass:: VMAction\n\n    .. js:autoattribute:: CONTINUE\n    .. js:autoattribute:: SKIP_INST\n    .. js:autoattribute:: SKIP_PATCH\n    .. js:autoattribute:: BREAK_TO_VM\n    .. js:autoattribute:: STOP\n\n.. js:autoclass:: InstPosition\n\n    .. js:autoattribute:: PREINST\n    .. js:autoattribute:: POSTINST\n\n.. js:autoclass:: CallbackPriority\n\n    .. js:autoattribute:: PRIORITY_DEFAULT\n    .. js:autoattribute:: PRIORITY_MEMACCESS_LIMIT\n\n.. _instanalysis-js:\n\nInstAnalysis\n------------\n\n.. js:autoclass:: AnalysisType\n\n    .. js:autoattribute:: ANALYSIS_INSTRUCTION\n    .. js:autoattribute:: ANALYSIS_DISASSEMBLY\n    .. js:autoattribute:: ANALYSIS_OPERANDS\n    .. js:autoattribute:: ANALYSIS_SYMBOL\n    .. js:autoattribute:: ANALYSIS_JIT\n\n\n.. js:class:: InstAnalysis\n\n  Object that describes the analysis of an instruction\n\n  .. js:attribute:: address\n\n      Instruction address (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: affectControlFlow\n\n      True if instruction affects control flow (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: disassembly\n\n      Instruction disassembly (if ANALYSIS_DISASSEMBLY)\n\n  .. js:attribute:: instSize\n\n      Instruction size (in bytes) (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: isBranch\n\n    True if instruction acts like a ‘jump’ (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: isCall\n\n    True if instruction acts like a ‘call’ (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: isCompare\n\n    True if instruction is a comparison (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: isPredicable\n\n    True if instruction contains a predicate (~is conditional) (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: isMoveImm\n\n    True if this instruction is a move immediate (including conditional moves) instruction (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: isReturn\n\n    True if instruction acts like a ‘return’ (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: mayLoad\n\n    True if QBDI detects a load for this instruction (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: mayStore\n\n    True if QBDI detects a store for this instruction (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: loadSize\n\n    size of the expected read access (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: storeSize\n\n    size of the expected written access (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: condition\n\n    Condition associated with the instruction (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: mnemonic\n\n    LLVM mnemonic (if ANALYSIS_INSTRUCTION)\n\n  .. js:attribute:: flagsAccess\n\n    Flag access type (noaccess, r, w, rw) (if ANALYSIS_OPERANDS)\n\n  .. js:attribute:: operands\n\n    Structure containing analysis results of an operand provided by the VM (if ANALYSIS_OPERANDS)\n\n  .. js:attribute:: moduleName\n\n    Instruction module name (if ANALYSIS_SYMBOL and found)\n\n  .. js:attribute:: symbolName\n\n    Instruction symbol (if ANALYSIS_SYMBOL and found)\n\n  .. js:attribute:: symbolOffset\n\n    Instruction symbol offset (if ANALYSIS_SYMBOL and found)\n\n  .. js:attribute:: patchAddress\n\n    Begin of the instrumentation patch for this instruction\n\n  .. js:attribute:: patchSize\n\n    Whole size of the instrumentation patch\n\n  .. js:attribute:: patchInstOffset\n\n    Offset of the JIT instruction in the path\n\n  .. js:attribute:: patchInstSize\n\n    Size of the JIT instruction in the path\n\n.. js:autoclass:: ConditionType\n\n  .. js:autoattribute:: CONDITION_NONE\n  .. js:autoattribute:: CONDITION_ALWAYS\n  .. js:autoattribute:: CONDITION_NEVER\n  .. js:autoattribute:: CONDITION_EQUALS\n  .. js:autoattribute:: CONDITION_NOT_EQUALS\n  .. js:autoattribute:: CONDITION_ABOVE\n  .. js:autoattribute:: CONDITION_BELOW_EQUALS\n  .. js:autoattribute:: CONDITION_ABOVE_EQUALS\n  .. js:autoattribute:: CONDITION_BELOW\n  .. js:autoattribute:: CONDITION_GREAT\n  .. js:autoattribute:: CONDITION_LESS_EQUALS\n  .. js:autoattribute:: CONDITION_GREAT_EQUALS\n  .. js:autoattribute:: CONDITION_LESS\n  .. js:autoattribute:: CONDITION_EVEN\n  .. js:autoattribute:: CONDITION_ODD\n  .. js:autoattribute:: CONDITION_OVERFLOW\n  .. js:autoattribute:: CONDITION_NOT_OVERFLOW\n  .. js:autoattribute:: CONDITION_SIGN\n  .. js:autoattribute:: CONDITION_NOT_SIGN\n\n.. js:class:: OperandAnalysis\n\n  Structure containing analysis results of an operand provided by the VM.\n\n  .. js:attribute:: type\n\n    Operand type\n\n  .. js:attribute:: flag\n\n    Operand flag\n\n  .. js:attribute:: value\n\n    Operand value (if immediate), or register Id\n\n  .. js:attribute:: size\n\n    Operand size (in bytes)\n\n  .. js:attribute:: regOff\n\n    Sub-register offset in register (in bits)\n\n  .. js:attribute:: regCtxIdx\n\n    Register index in VM state\n\n  .. js:attribute:: regName\n\n    Register name\n\n  .. js:attribute:: regAccess\n\n    Register access type (r, w, rw)\n\n.. js:autoclass:: OperandType\n\n    .. js:autoattribute:: OPERAND_INVALID\n    .. js:autoattribute:: OPERAND_IMM\n    .. js:autoattribute:: OPERAND_GPR\n    .. js:autoattribute:: OPERAND_PRED\n    .. js:autoattribute:: OPERAND_FPR\n    .. js:autoattribute:: OPERAND_SEG\n\n.. js:autoclass:: OperandFlag\n\n    .. js:autoattribute:: OPERANDFLAG_NONE\n    .. js:autoattribute:: OPERANDFLAG_ADDR\n    .. js:autoattribute:: OPERANDFLAG_PCREL\n    .. js:autoattribute:: OPERANDFLAG_UNDEFINED_EFFECT\n    .. js:autoattribute:: OPERANDFLAG_IMPLICIT\n\n.. js:autoclass:: RegisterAccessType\n\n    .. js:autoattribute:: REGISTER_READ\n    .. js:autoattribute:: REGISTER_WRITE\n    .. js:autoattribute:: REGISTER_READ_WRITE\n\n.. _memaccess-js:\n\nMemoryAccess\n------------\n\n.. js:class:: MemoryAccess\n\n  Object that describes a memory access\n\n  .. js:attribute:: accessAddress\n\n      Address of accessed memory\n\n  .. js:attribute:: instAddress\n\n      Address of instruction making the access\n\n  .. js:attribute:: size\n\n      Size of memory access (in bytes)\n\n  .. js:attribute:: type\n\n      Memory access type (READ / WRITE)\n\n  .. js:attribute:: value\n\n      Value read from / written to memory\n\n  .. js:attribute:: flags\n\n      Memory access flags\n\n.. js:autoclass:: MemoryAccessType\n\n    .. js:autoattribute:: MEMORY_READ\n    .. js:autoattribute:: MEMORY_WRITE\n    .. js:autoattribute:: MEMORY_READ_WRITE\n\n.. js:autoclass:: MemoryAccessFlags\n\n    .. js:autoattribute:: MEMORY_NO_FLAGS\n    .. js:autoattribute:: MEMORY_UNKNOWN_SIZE\n    .. js:autoattribute:: MEMORY_MINIMUM_SIZE\n    .. js:autoattribute:: MEMORY_UNKNOWN_VALUE\n\n.. _vmevent-js:\n\nVMEvent\n-------\n\n.. js:autoclass:: VMEvent\n\n    .. js:autoattribute:: SEQUENCE_ENTRY\n    .. js:autoattribute:: SEQUENCE_EXIT\n    .. js:autoattribute:: BASIC_BLOCK_ENTRY\n    .. js:autoattribute:: BASIC_BLOCK_EXIT\n    .. js:autoattribute:: BASIC_BLOCK_NEW\n    .. js:autoattribute:: EXEC_TRANSFER_CALL\n    .. js:autoattribute:: EXEC_TRANSFER_RETURN\n    .. js:autoattribute:: SYSCALL_ENTRY\n    .. js:autoattribute:: SYSCALL_EXIT\n    .. js:autoattribute:: SIGNAL\n\n.. js:class:: VMState\n\n  Object that describes the current VM state\n\n  .. js:attribute:: event\n\n    The event(s) which triggered the callback (must be checked using a mask: event & BASIC_BLOCK_ENTRY).\n\n  .. js:attribute:: sequenceStart\n\n    The current basic block start address which can also be the execution transfer destination.\n\n  .. js:attribute:: sequenceEnd\n\n    The current basic block end address which can also be the execution transfer destination.\n\n  .. js:attribute:: basicBlockStart\n\n    The current sequence start address which can also be the execution transfer destination.\n\n  .. js:attribute:: basicBlockEnd\n\n    The current sequence end address which can also be the execution transfer destination.\n\n  .. js:attribute:: lastSignal\n\n    Not implemented.\n\nOther globals\n-------------\n\n.. js:autoattribute:: QBDI_LIB_FULLPATH\n\n.. js:autoclass:: Options\n\n    .. js:autoattribute:: NO_OPT\n    .. js:autoattribute:: OPT_DISABLE_FPR\n    .. js:autoattribute:: OPT_DISABLE_OPTIONAL_FPR\n    .. js:autoattribute:: OPT_DISABLE_MEMORYACCESS_VALUE\n    .. js:autoattribute:: OPT_DISABLE_ERRNO_BACKUP\n    .. js:autoattribute:: OPT_ATT_SYNTAX\n    .. js:autoattribute:: OPT_ENABLE_FS_GS\n\n.. js:autoclass:: VMError\n\n    .. js:autoattribute:: INVALID_EVENTID\n\nRegister values\n---------------\n\nThe size of a general register depends of the architecture.\nQBDI uses a custom type (:js:data:`rword`) to represent a register value.\n\nThis binding provides a common interface (``.toRword()``) to cast values into JS types compatible\nwith the C :js:data:`rword` type.\n\n\n.. js:autoattribute:: rword\n\n.. js:function:: NativePointer.prototype.toRword()\n\n    Convert a NativePointer into a type with the size of a register (``Number`` or ``UInt64``).\n\n.. js:function:: Number.prototype.toRword()\n\n   Convert a number into a type with the size of a register (``Number`` or ``UInt64``).\n   Can't be used for numbers > 32 bits would cause weird results due to IEEE-754.\n\n.. js:function:: UInt64.prototype.toRword()\n\n   An identity function (returning the same ``UInt64`` object).\n   It exists only to provide a unified **toRword** interface.\n\n\nHelpers\n-------\n\nSome functions helpful to interact with Frida’s interface and write scripts.\n\n.. js:autofunction:: hexPointer\n\n"
  },
  {
    "path": "docs/source/api_pyqbdi.rst",
    "content": ".. currentmodule:: pyqbdi\n\nPyQBDI API\n==========\n\nIntroduction\n------------\n\nWe offer bindings for Python, the whole C/C++ API is available through them, but they are also backed up by plenty of helpers that fluidify the script writing.\n\nOn Linux, macOS and Windows, PyQBDI supports QBDIPreload as :ref:`PyQBDIPreload <pyqbdipreload_api>`.\n\nHowever, you must be aware that PyQBDI has some limitations:\n\n- The library can handle only one :py:class:`VM` at a time.\n- The :py:class:`VM` must not be used in the ``atexit`` module.\n- 32-bit versions of PyQBDI and Python are needed to instrument 32-bit targets.\n- PyQBDI cannot be used to instrument a Python process since both the **host** and the **target** will use the Python runtime.\n\nVM class\n--------\n\n.. autoclass:: pyqbdi.VM\n    :special-members: __init__\n    :members:\n    :exclude-members: getGPRState, getFPRState, getErrno, setGPRState, setFPRState, setErrno,\n                      addInstrumentedRange, addInstrumentedModule, addInstrumentedModuleFromAddr, instrumentAllExecutableMaps,\n                      removeInstrumentedRange, removeInstrumentedModule, removeInstrumentedModuleFromAddr, removeAllInstrumentedRanges,\n                      addCodeCB, addCodeAddrCB, addCodeRangeCB, addMnemonicCB, addVMEventCB, addMemAccessCB, addMemAddrCB, addMemRangeCB,\n                      recordMemoryAccess, addInstrRule, addInstrRuleRange, deleteInstrumentation, deleteAllInstrumentations, run, call,\n                      getInstAnalysis, getCachedInstAnalysis, getInstMemoryAccess, getBBMemoryAccess, precacheBasicBlock, clearCache, clearAllCache,\n                      reduceCacheTo, getNbExecBlock, getJITInstAnalysis\n\n.. _state-management-pyqbdi:\n\nState management\n++++++++++++++++\n\n.. autofunction:: pyqbdi.VM.getGPRState\n\n.. autofunction:: pyqbdi.VM.getFPRState\n\n.. autofunction:: pyqbdi.VM.getErrno\n\n.. autofunction:: pyqbdi.VM.setGPRState\n\n.. autofunction:: pyqbdi.VM.setFPRState\n\n.. autofunction:: pyqbdi.VM.setErrno\n\n.. _instrumentation-range-pyqbdi:\n\nInstrumentation range\n+++++++++++++++++++++\n\nAddition\n^^^^^^^^\n\n.. autofunction:: pyqbdi.VM.addInstrumentedRange\n\n.. autofunction:: pyqbdi.VM.addInstrumentedModule\n\n.. autofunction:: pyqbdi.VM.addInstrumentedModuleFromAddr\n\n.. autofunction:: pyqbdi.VM.instrumentAllExecutableMaps\n\nRemoval\n^^^^^^^\n\n.. autofunction:: pyqbdi.VM.removeInstrumentedRange\n\n.. autofunction:: pyqbdi.VM.removeInstrumentedModule\n\n.. autofunction:: pyqbdi.VM.removeInstrumentedModuleFromAddr\n\n.. autofunction:: pyqbdi.VM.removeAllInstrumentedRanges\n\nCallback management\n+++++++++++++++++++\n\n.. _instcallback-management-pyqbdi:\n\nInstCallback\n^^^^^^^^^^^^\n\n.. autofunction:: pyqbdi.VM.addCodeCB\n\n.. autofunction:: pyqbdi.VM.addCodeAddrCB\n\n.. autofunction:: pyqbdi.VM.addCodeRangeCB\n\n.. autofunction:: pyqbdi.VM.addMnemonicCB\n\n.. _vmcallback-management-pyqbdi:\n\nVMEvent\n^^^^^^^\n\n.. autofunction:: pyqbdi.VM.addVMEventCB\n\n.. _memorycallback-management-pyqbdi:\n\nMemoryAccess\n^^^^^^^^^^^^\n\n.. autofunction:: pyqbdi.VM.addMemAccessCB\n\n.. autofunction:: pyqbdi.VM.addMemAddrCB\n\n.. autofunction:: pyqbdi.VM.addMemRangeCB\n\n.. _instrrulecallback-management-pyqbdi:\n\nInstrRuleCallback\n^^^^^^^^^^^^^^^^^\n\n.. autofunction:: pyqbdi.VM.addInstrRule\n\n.. autofunction:: pyqbdi.VM.addInstrRuleRange\n\nRemoval\n^^^^^^^\n\n.. autofunction:: pyqbdi.VM.deleteInstrumentation\n\n.. autofunction:: pyqbdi.VM.deleteAllInstrumentations\n\nRun\n+++\n\n.. autofunction:: pyqbdi.VM.run\n\n.. autofunction:: pyqbdi.VM.call\n\n.. _instanalysis-getter-pyqbdi:\n\nInstAnalysis\n++++++++++++\n\n.. autofunction:: pyqbdi.VM.getInstAnalysis\n\n.. autofunction:: pyqbdi.VM.getCachedInstAnalysis\n\n.. autofunction:: pyqbdi.VM.getJITInstAnalysis\n\n.. _memaccess-getter-pyqbdi:\n\nMemoryAccess\n++++++++++++\n\n.. autofunction:: pyqbdi.VM.getInstMemoryAccess\n\n.. autofunction:: pyqbdi.VM.getBBMemoryAccess\n\n.. autofunction:: pyqbdi.VM.recordMemoryAccess\n\nCache management\n++++++++++++++++\n\n.. autofunction:: pyqbdi.VM.precacheBasicBlock\n\n.. autofunction:: pyqbdi.VM.clearCache\n\n.. autofunction:: pyqbdi.VM.clearAllCache\n\n.. autofunction:: pyqbdi.VM.getNbExecBlock\n\n.. autofunction:: pyqbdi.VM.reduceCacheTo\n\n.. _register-state-pyqbdi:\n\nRegister state\n--------------\n\nThis documentation only shows the register for the X86_64 architectures. For ARM\nand AARCH64, the GPRState and FPRState are similar to the C/C++\n:cpp:class:`QBDI::GPRState` and :cpp:class:`QBDI::FPRState`.\n\n.. autoclass:: pyqbdi.GPRState\n    :members:\n    :undoc-members:\n    :special-members: __getitem__, __setitem__\n\n.. autoclass:: pyqbdi.FPRState\n    :members:\n    :undoc-members:\n\n.. data:: pyqbdi.REG_RETURN\n\n.. data:: pyqbdi.REG_BP\n\n.. data:: pyqbdi.REG_SP\n\n.. data:: pyqbdi.REG_PC\n\n.. data:: pyqbdi.NUM_GPR\n\n.. _callback-pyqbdi:\n\nCallback\n--------\n\n.. function:: pyqbdi.InstCallback(vm: pyqbdi.VM, gpr: pyqbdi.GPRState, fpr: pyqbdi.FPRState, data: object) -> pyqbdi.VMAction\n\n    This is the prototype of a function callback for:\n\n    - :py:func:`pyqbdi.VM.addCodeCB`, :py:func:`pyqbdi.VM.addCodeAddrCB` and :py:func:`pyqbdi.VM.addCodeRangeCB`\n    - :py:func:`pyqbdi.VM.addMnemonicCB`\n    - :py:func:`pyqbdi.VM.addMemAccessCB`, :py:func:`pyqbdi.VM.addMemAddrCB` and :py:func:`pyqbdi.VM.addMemRangeCB`\n    - :py:class:`pyqbdi.InstrRuleDataCBK`.\n\n    :param VM       vm:    The current QBDI object\n    :param GPRState gpr:   The current GPRState\n    :param FPRState fpr:   The current FPRState\n    :param Object   data:  A user-defined object.\n\n    :return: the :py:data:`VMAction` to continue or stop the execution\n\n.. function:: pyqbdi.VMCallback(vm: pyqbdi.VM, vmState: pyqbdi.VMState, gpr: pyqbdi.GPRState, fpr: pyqbdi.FPRState, data: object) -> pyqbdi.VMAction\n\n    This is the prototype of a function callback for :py:func:`pyqbdi.VM.addVMEventCB`.\n\n    :param VM       vm:      The current QBDI object\n    :param VMState  vmState: A structure containing the current state of the VM.\n    :param GPRState gpr:     The current GPRState\n    :param FPRState fpr:     The current FPRState\n    :param Object   data:    A user-defined object\n\n    :return: the :py:data:`VMAction` to continue or stop the execution\n\n.. function:: pyqbdi.InstrRuleCallback(vm: pyqbdi.VM, ana: pyqbdi.InstAnalysis, data: object) -> List[pyqbdi.InstrRuleDataCBK]\n\n    This is the prototype of a function callback for :py:func:`pyqbdi.VM.addInstrRule` and :py:func:`pyqbdi.VM.addInstrRuleRange`.\n\n    :param VM           vm:   The current QBDI object\n    :param InstAnalysis ana:  The current QBDI object\n    :param Object       data: A user-defined object\n\n    :return: A list of :py:class:`pyqbdi.InstrRuleDataCBK`\n\n.. autoclass:: pyqbdi.InstrRuleDataCBK\n    :special-members: __init__\n    :members:\n\n.. autodata:: pyqbdi.InstPosition\n\n.. autodata:: pyqbdi.CallbackPriority\n\n.. autodata:: pyqbdi.VMAction\n\n.. _instanalysis-pyqbdi:\n\nInstAnalysis\n------------\n\n.. autodata:: pyqbdi.AnalysisType\n\n.. autoclass:: pyqbdi.InstAnalysis\n    :members:\n\n.. autodata:: pyqbdi.ConditionType\n\n.. autoclass:: pyqbdi.OperandAnalysis\n    :members:\n\n.. autodata:: pyqbdi.OperandType\n\n.. autodata:: pyqbdi.OperandFlag\n\n.. autodata:: pyqbdi.RegisterAccessType\n\n.. _memaccess-pyqbdi:\n\nMemoryAccess\n------------\n\n.. autoclass:: pyqbdi.MemoryAccess\n    :members:\n\n.. autodata:: pyqbdi.MemoryAccessType\n\n.. autodata:: pyqbdi.MemoryAccessFlags\n\n.. _vmevent-pyqbdi:\n\nVMEvent\n-------\n\n.. autodata:: pyqbdi.VMEvent\n\n.. autoclass:: pyqbdi.VMState\n    :members:\n\nMemory management\n-----------------\n\nAllocation\n++++++++++\n\n.. autofunction:: pyqbdi.alignedAlloc\n\n.. autofunction:: pyqbdi.allocateVirtualStack\n\n.. autofunction:: pyqbdi.alignedFree\n\n.. autofunction:: pyqbdi.simulateCall\n\nExploration\n+++++++++++\n\n.. autofunction:: pyqbdi.getModuleNames\n\n.. autofunction:: pyqbdi.getCurrentProcessMaps\n\n.. autofunction:: pyqbdi.getRemoteProcessMaps\n\n.. autoclass:: pyqbdi.MemoryMap\n    :members:\n\n.. autodata:: pyqbdi.Permission\n\nOther globals\n-------------\n\nThis documentation only shows the options available for the X86_64 architectures. For ARM\nand AARCH64, refer the C/C++ API :cpp:enum:`QBDI::Options`.\n\n.. autodata:: pyqbdi.Options\n\n.. autodata:: pyqbdi.VMError\n\nMiscellaneous\n-------------\n\nVersion & info\n++++++++++++++\n\n.. data:: pyqbdi.__arch__\n\n.. data:: pyqbdi.__platform__\n\n.. data:: pyqbdi.__preload__\n\n    Library load with pyqbdipreload\n\n.. data:: pyqbdi.__version__\n\n    Version of QBDI\n\nLog\n+++\n\n.. autodata:: pyqbdi.LogPriority\n\n.. autofunction:: pyqbdi.setLogPriority\n\nRange\n+++++\n\n.. autoclass:: pyqbdi.Range\n    :members:\n\nMemory helpers\n--------------\n\n.. autofunction:: pyqbdi.readMemory\n.. autofunction:: pyqbdi.readRword\n.. autofunction:: pyqbdi.writeMemory\n.. autofunction:: pyqbdi.writeRword\n.. autofunction:: pyqbdi.allocateRword\n.. autofunction:: pyqbdi.allocateMemory\n.. autofunction:: pyqbdi.freeMemory\n\nFloat helpers\n-------------\n\n.. autofunction:: pyqbdi.encodeFloat\n.. autofunction:: pyqbdi.decodeFloat\n.. autofunction:: pyqbdi.encodeFloatU\n.. autofunction:: pyqbdi.decodeFloatU\n.. autofunction:: pyqbdi.encodeDouble\n.. autofunction:: pyqbdi.decodeDouble\n.. autofunction:: pyqbdi.encodeDoubleU\n.. autofunction:: pyqbdi.decodeDoubleU\n\nFor more conversion utilities, check out the Python's `struct library <https://docs.python.org/3/library/struct.html>`_.\n"
  },
  {
    "path": "docs/source/api_pyqbdipreload.rst",
    "content": ".. _pyqbdipreload_api:\n\nPyQBDIPreload API\n=================\n\nIntroduction\n------------\n\nPyQBDIPreload consists of two main components:\n\n- The QBDIPreload script included in the PyQBDI library.\n  It will load the Python runtime and execute the user script in order to instrument the target.\n- An injector script called `pyqbdipreload.py` that sets up the environment variables to inject the library and the Python runtime.\n\n.. note::\n    PyQBDIPreload has the same limitations as QBDIPreload and PyQBDI.\n\nUser callback\n-------------\n\n.. function:: pyqbdipreload_on_run(vm: pyqbdi.VM, start: int, end: int)\n\n    This function must be present in the preloaded script\n\n    :param VM vm:     VM Instance\n    :param int start: Start address of the range (included).\n    :param int end:   End address of the range (excluded).\n\nQBDIPreload steps\n-----------------\n\n:cpp:func:`qbdipreload_on_start` and :cpp:func:`qbdipreload_on_premain` are not available on PyQBDIPreload.\n\nThe Python interpreter and the preloaded script are loaded while executing :cpp:func:`qbdipreload_on_main`.\n\nThe :py:func:`pyqbdipreload_on_run` method is called while executing :cpp:func:`qbdipreload_on_main`.\n\nThe interpreter is shut down while executing :cpp:func:`qbdipreload_on_exit`, after calling all the registered methods of\n`atexit <https://docs.python.org/fr/3/library/atexit.html>`_ module.\n\nPyQBDIPreload script\n--------------------\n\nYou can use `pyqbdipreload.py` as follows:\n\n.. code-block:: bash\n\n    $ python3 -m pyqbdipreload <script> <target> [<args> ...]\n\nwith:\n\n- ``script``: The Python script\n- ``target``: The binary you are targeting\n- ``args``: Argument(s) to be passed to the binary (if any)\n"
  },
  {
    "path": "docs/source/api_qbdipreload.rst",
    "content": ".. _qbdipreload_api:\n\nQBDIPreload API\n===============\n\nIntroduction\n------------\n\nQBDIPreload is a small utility library that provides code injection capabilities using dynamic\nlibrary injection.\n\nQBDIPreload exploits library injection mechanisms to hijack the normal program startup.\nDuring the hijacking process QBDIPreload will call your code allowing you to setup and start\nyour instrumentation. The compilation should produce a dynamic library (``.so`` under **Linux**,\n``.dylib`` under **macOS**, ``.dll`` under **Windows**) which should then be injected into your\ntarget executable (``LD_PRELOAD`` under **Linux**, ``DYLD_INSERT_LIBRARIES`` under **macOS**,\n``QBDIWinPreloader.exe`` under **Windows** or a dll injection technique of your choice).\n\nYou can look at :ref:`qbdi_preload_template` for a working example with build and usage\ninstructions.\n\n.. note::\n   QBDIPreload automatically takes care of blacklisting instrumentation of the C standard library\n   and the OS loader as described in :ref:`intro_limitations`.\n\n.. note::\n   Please note that QBDIPreload does not allow instrumenting a binary before the main function\n   (inside the loader and the library constructors / init) as explained in :ref:`intro_limitations`.\n\n.. note::\n   On Linux and macOS QBDIPreload is supposed to be used with ``LD_PRELOAD`` or ``DYLD_INSERT_LIBRARIES``\n   mechanisms to inject some code into the target process. Hence, the limitations of these also affect\n   QBDIPreload (cannot inject suid binary, ...).\n\n\nInitialisation\n--------------\n\n.. doxygendefine:: QBDIPRELOAD_INIT\n    :project: QBDIPRELOAD\n\n\nReturn codes\n------------\n\n.. doxygendefine:: QBDIPRELOAD_NO_ERROR\n    :project: QBDIPRELOAD\n\n.. doxygendefine:: QBDIPRELOAD_NOT_HANDLED\n    :project: QBDIPRELOAD\n\n.. doxygendefine:: QBDIPRELOAD_ERR_STARTUP_FAILED\n    :project: QBDIPRELOAD\n\nUser callbacks\n--------------\n\n.. doxygenfunction:: qbdipreload_on_start\n    :project: QBDIPRELOAD\n\n.. doxygenfunction:: qbdipreload_on_premain\n    :project: QBDIPRELOAD\n\n.. doxygenfunction:: qbdipreload_on_main\n    :project: QBDIPRELOAD\n\n.. doxygenfunction:: qbdipreload_on_run\n    :project: QBDIPRELOAD\n\n.. doxygenfunction:: qbdipreload_on_exit\n    :project: QBDIPRELOAD\n\nHelpers\n-------\n\n.. doxygenfunction:: qbdipreload_hook_main\n    :project: QBDIPRELOAD\n\n.. doxygenfunction:: qbdipreload_threadCtxToGPRState\n    :project: QBDIPRELOAD\n\n.. doxygenfunction:: qbdipreload_floatCtxToFPRState\n    :project: QBDIPRELOAD\n"
  },
  {
    "path": "docs/source/architecture_support.rst",
    "content": "Architecture Support\n====================\n\nOur patching system uses a generic approach which means instruction support is not explicit but\nimplicit. Only instructions that use the program counter register (``rip`` under x86-64 and ``pc``\nunder ARM) or modify the control flow needs patching and we hope the current patching rules cover\nall such cases. However some corner cases or bugs in the disassembly and assembly backend of LLVM\nmight still cause troubles.\n\nTo guarantee that instrumented programs run smoothly and that no such bugs exist, we are running\nautomated tests on a wide variety of binaries (see :ref:`developer-testing` for more details). From\nthose tests we can establish an instruction coverage which provides an estimation of the instructions\nsupported by QBDI. This estimation is far from complete because the other instructions were simply\nprobably never encountered in the test\nsuite.\n\nInstructions below are listed using their mnemonic (or LLVM MC opcode). This naming convention distinguishes\nbetween size and operand variants of the same mnemonic: ``ADD64rm`` is a 64 bits add of a memory\nvalue to a register while ``ADD32ri8`` is a 32 bits add of an 8 bits immediate to a register.\n\nAARCH64\n-------\n\nThe AARCH64 support with the Memory Access.\nThe support is more recent than X86_64 and some bugs should be expected.\n\nSome extensions are not supported by QBDI, in particular:\n\n- Scalable Vector Extension (SVE and SVE2)\n- Scalable Matrix Extension (SME)\n- Memory Tagging Extension (MTE)\n- Transactional Memory Extension (TME)\n\nFor macOS, the register X18 is defined as platform reserved. QBDI doesn't set or\nuse this register.\n\nLocal Monitor\n^^^^^^^^^^^^^\n\nThe AARCH64 support includes an emulator of the Local Monitor. You can disable it\nwith the option :cpp:enumerator:`OPT_DISABLE_LOCAL_MONITOR`.\n\nPointer Authentication\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nQBDI supports the instrumentation of code using authentication pointer.\nAs different types of authentication are possible (ie, keyA vs keyB, instruction\nvs data, modifier, ...), QBDI only handle unauthenticated internally. As it,\npointers should be unauthenticated when:\n\n- Setting the register PC in :cpp:struct:`QBDI::GPRState`,\n- Setting the instrumentation range,\n- Create an instruction or memory callback by specifying an address or a range of address,\n- Beginning the instrumentation by using :cpp:func:`QBDI::VM::call`, :cpp:func:`QBDI::VM::run` or similar functions,\n- Retrieving the :cpp:struct:`QBDI::InstAnalysis` using\n  :cpp:func:`QBDI::VM::getCachedInstAnalysis` or\n  :cpp:func:`QBDI::VM::getJITInstAnalysis`\n- Managed the cache (with :cpp:func:`QBDI::VM::precacheBasicBlock` or :cpp:func:`QBDI::VM::clearCache`)\n\nQBDI will always report unauthenticated address, especially:\n\n- In PC register of :cpp:struct:`QBDI::GPRState`,\n- In the :cpp:struct:`QBDI::InstAnalysis` structure,\n- In :cpp:struct:`QBDI::MemoryAccess` structure,\n- during VMEvent in the :cpp:struct:`QBDI::VMState` structure\n\nOn macOS, QBDI should be compiled with the same architectures (ie, ``arm64`` or\n``arm64e``) as the target (by settings the CMake parameter ``-DCMAKE_OSX_ARCHITECTURES=\"arm64e\"``).\n\nAt the compile time, the option ``-DQBDI_PTRAUTH=ON`` can be enabled to\nstrip the authentication of any pointer that should be given\nunauthenticated by the user.\n\nInstruction Coverage\n^^^^^^^^^^^^^^^^^^^^\n\nTODO\n\nARMv7\n-----\n\nThe ARMv7 has been refactored to support both ARM and Thumb mode with the Memory Access.\nThe support is more recent than X86_64 and some bugs should be expected.\n\nThumb Mode\n^^^^^^^^^^\n\nWhen the CPU is in Thumb Mode, the LSB of ``GPRState.PC`` is set to 1. However,\nQBDI doesn't set the corresponding byte in ``GPRState.CPSR``. In addition, when\nan InstAnalysis is provided for a Thumb instruction, the ``address`` field\nalways has its LSB set to 0 and the field ``cpuMode`` must be used to distinguish\nan ARM instruction from a Thumb instruction.\n\nQBDI supports the Thumb2 instruction IT. However, this instruction is handled\ninternally. The ``GPRState.CPSR`` doesn't include the ITSTATE. To avoid\nundefined behavior, you must not:\n- Clear the VM cache during the execution of an ITBlock.\n- Start or restart the execution of QBDI in the middle of a ITBlock.\n\nLocal Monitor\n^^^^^^^^^^^^^\n\nThe ARM support includes an emulator of the Local Monitor. You can disable it\nwith the option :cpp:enumerator:`OPT_DISABLE_LOCAL_MONITOR`.\n\nInstruction Coverage\n^^^^^^^^^^^^^^^^^^^^\n\n.. code-block::\n\n    ADCri\n    ADDri ADDrr ADDrsi\n    ANDri\n    Bcc BL BX_RET\n    CMNri\n    CMPri CMPrr\n    EORri\n    FCONSTD\n    FMSTAT\n    LDMIA_UPD\n    LDRBi12 LDRD LDRH LDRi12 LDR_PRE_IMM LDRrs\n    MOVi MOVr MOVsi\n    MVNi\n    ORRri ORRrr\n    STMDB_UPD\n    STRBi12 STRBrs STRD STRi12\n    SUBri\n    UBFX\n    UXTB UXTH\n    VABSD VADD VCMPD VCMPE VCMPEZD VDIVD\n    VLDMDIA_UPD VLDRD VLDRS\n    VMLAD VMOVD VMOVDRR VMOVRRD\n    VMOVRS VMOVSR VMULD VNMLSD VSITOD\n    VSTMDDB_UPD VSTRD VSTRS\n    VSUBD VTOSIZD VTOUIZD VUITOD\n\n    tADC t2ADCri t2ADCrr t2ADCrs\n    tADDhirr tADDi3 tADDi8 tADDrr tADDrSPi tADDspi t2ADDri t2ADDri12 t2ADDrr t2ADDrs t2ADDspImm t2ADDspImm12\n    tADR t2ADR\n    tAND t2ANDri t2ANDrr t2ANDrs\n    tASRri tASRrr t2ASRri t2ASRrr\n    tB tBcc tBL tBLXi tBLXr tBX t2B t2Bcc\n    t2BICri t2BICrr t2BICrs\n    t2CLZ\n    tCBNZ tCBZ\n    tCMNz t2CMNri t2CMNzrr\n    tCMPhir tCMPi8 tCMPr t2CMPri t2CMPrr t2CMPrs\n    t2DMB\n    tEOR t2EORri t2EORrr t2EORrs\n    tHINT t2HINT\n    t2IT\n    tLDMIA t2LDMIA t2LDMIA_UPD\n    t2LDRBi12 t2LDRBi8\n    tLDRBi tLDRBr t2LDRB_POST t2LDRB_PRE t2LDRBs\n    t2LDRDi8\n    t2LDREX\n    tLDRHi tLDRHr t2LDRHi12 t2LDRHi8 t2LDRH_POST t2LDRHs\n    tLDRi tLDRpci tLDRr tLDRspi t2LDRi12 t2LDRi8 t2LDRpci t2LDR_POST t2LDR_PRE t2LDRs\n    tLDRSB t2LDRSBi12\n    tLSLri tLSLrr tLSRri tLSRrr t2LSLri t2LSLrr t2LSRri t2LSRrr\n    t2MLA t2MLS\n    tMOVi8 tMOVr tMOVSr t2MOVi t2MOVi16 t2MOVTi16\n    t2MRC\n    t2MUL\n    tMVN t2MVNi t2MVNr\n    t2ORNri t2ORNrr\n    tORR t2ORRri t2ORRrr t2ORRrs\n    tPOP\n    tPUSH\n    tREV tREV16 t2REV\n    t2RORri t2RSBri\n    tRSB\n    tSBC t2SBCri t2SBCrr t2SBCrs\n    t2SMULL\n    tSTMIA_UPD t2STMDB_UPD t2STMIA t2STMIA_UPD\n    tSTRBi tSTRBr t2STRBi12 t2STRBi8 t2STRB_POST t2STRB_PRE t2STRBs\n    t2STRDi8 t2STRD_PRE\n    t2STREX\n    tSTRHi tSTRHr t2STRHi12 t2STRHi8 t2STRH_PRE t2STRHs\n    tSTRi TSTri tSTRr tSTRspi t2STRi12 t2STRi8 t2STR_POST t2STR_PRE t2STRs\n    tSUBi3 tSUBi8 tSUBrr tSUBspi t2SUBri t2SUBri12 t2SUBrr t2SUBrs t2SUBspImm t2SUBspImm12\n    tSXTB tSXTH\n    t2TBB t2TBH\n    t2TEQri\n    tTST t2TSTri t2TSTrr\n    t2UBFX\n    t2UMLAL t2UMULL\n    tUXTB\n    tUXTH\n    t2UXTAH t2UXTB\n\nIntel x86-64\n------------\n\nThe x86-64 support is complete and mature. Only a small part of SIMD instructions are covered\nby our tests but we do not expect any problems with the uncovered ones because their semantics are\nclosely related to the covered ones. We currently don't support the following features:\n\n- AVX512: the register of this extension isn't supported and will not be restored/backup during the execution\n- privileged instruction: QBDI is a userland (ring3) application and privileged registers aren't managed\n- CET feature: shadow stack is not implemented and the current instrumentation doesn't support\n  indirect branch tracking.\n- HLE and RTM features and any instructions for multithreading: QBDI allows inserting callbacks\n  at any position and cannot guarantee that instructions of the same transactions unit will not be split.\n- MPX feature: bound registers aren't supported.\n- XOP instructions for AMD processors: The instructions are never been tested.\n- APX instructions are currently not tested and additional GPR register\n  (R16-R31) are not backed up by QBDI.\n\nIf one of these features become well used, it may be integrated to QBDI to a future\nversion.\n\nThe memory access information is provided for most of general and SIMD instructions.\nThe information is missing for:\n\n- The instructions included in an unsupported feature\n- VGATHER* and VPGATHER* instructions of AVX2.\n\nInstruction Coverage\n^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: bash\n\n    ADC32mi8 ADC64mi8\n    ADD16mi8 ADD16mr ADD16rm ADD32i32 ADD32mi8 ADD32mr ADD32ri ADD32ri8 ADD32rm ADD32rr ADD64i32 ADD64mi32 ADD64mi8 ADD64mr ADD64ri32 ADD64ri8 ADD64rm ADD64rr ADD8rr ADDSDrm_Int ADDSDrr_Int\n    AESENCLASTrr AESENCrr\n    AND16mi AND16mr AND32i32 AND32mi8 AND32mr AND32ri AND32ri8 AND32rm AND32rr AND64mi8 AND64mr AND64ri8 AND64rr AND8mi AND8mr AND8ri AND8rm AND8rr ANDNPDrr ANDPDrm ANDPDrr\n    BSWAP32r BSWAP64r BT64rr\n    CALL64m CALL64pcrel32 CALL64r\n    CDQ CDQE\n    CMOV32rm CMOV32rr CMOV64rm CMOV64rr\n    CMP16mi8 CMP16mr CMP16rm CMP16rr CMP32i32 CMP32mi CMP32mi8 CMP32mr CMP32ri CMP32ri8 CMP32rm CMP32rr CMP64i32 CMP64mi32 CMP64mi8 CMP64mr CMP64ri32 CMP64ri8 CMP64rm CMP64rr CMP8i8 CMP8mi CMP8mr CMP8ri CMP8rm CMP8rr\n    CMPSB\n    CMPSDrr_Int\n    CMPXCHG32rm CMPXCHG64rm\n    COMISDrm COMISDrr\n    CQO\n    CVTSI2SDrr_Int CVTSI642SDrm_Int CVTSI642SDrr_Int CVTTSD2SI64rr_Int CVTTSD2SIrr_Int\n    DEC32m DEC32r\n    DIV32r DIV64m DIV64r DIVSDrm_Int DIVSDrr_Int\n    IDIV32m IDIV32r IDIV64r\n    IMUL32r IMUL32rm IMUL32rr IMUL64rmi8 IMUL64rr IMUL64rri32\n    JCC_1 JCC_4\n    JMP64m JMP64r\n    JMP_1 JMP_4\n    LEA64_32r LEA64r\n    MFENCE\n    MOV16mi MOV16mr MOV32mi MOV32mr MOV32ri MOV32rm MOV32rr MOV64mi32 MOV64mr MOV64ri MOV64ri32 MOV64rm MOV64rr MOV8mi MOV8mr\n    MOVAPDrr MOVAPSmr\n    MOVDQArm MOVDQArr\n    MOVDQUmr MOVDQUrm\n    MOVQI2PQIrm\n    MOVSDmr MOVSDrm\n    MOVSL MOVSQ\n    MOVSX32rm8 MOVSX32rr8 MOVSX64rm32 MOVSX64rm8 MOVSX64rr16 MOVSX64rr32 MOVSX64rr8\n    MOVUPSmr MOVUPSrm\n    MOVZX32rm16 MOVZX32rm8 MOVZX32rr16 MOVZX32rr8\n    MUL32r MUL64r MULSDrr_Int\n    NEG32r NEG64r\n    NOOP NOOP NOOPW\n    NOT32r NOT64m NOT64r\n    OR16rm OR32i32 OR32mi OR32mi8 OR32mr OR32ri OR32ri8 OR32rm OR32rr OR64i32 OR64ri8 OR64rm OR64rr OR8i8 OR8mi OR8mr OR8ri OR8rm OR8rr\n    ORPDrr\n    POP64r\n    PSHUFBrr PSHUFDri\n    PSLLDQri PSLLDri\n    PUSH64i8 PUSH64r PUSH64rmm\n    PXORrr\n    RETQ\n    ROL32r1 ROL32ri ROL64r1 ROL64ri\n    ROR32ri ROR64r1 ROR64ri\n    ROUNDSDr_Int\n    SAR32r1 SAR32rCL SAR32ri SAR64r1 SAR64ri\n    SBB32ri8 SBB32rr SBB64ri8 SBB64rr SBB8i8 SBB8ri\n    SCASB\n    SETCCm SETCCr\n    SHL32rCL SHL32ri SHL64rCL SHL64ri\n    SHR16ri SHR32r1 SHR32rCL SHR32ri SHR64r1 SHR64rCL SHR64ri SHR8r1 SHR8ri\n    STOSQ\n    SUB32mi8 SUB32mr SUB32ri SUB32ri8 SUB32rm SUB32rr SUB64mi8 SUB64mr SUB64ri32 SUB64ri8 SUB64rm SUB64rr SUB8mr SUBSDrm_Int SUBSDrr_Int\n    SYSCALL\n    TEST16mi TEST16ri TEST16rr TEST32i32 TEST32mi TEST32mr TEST32ri TEST32rr TEST64ri32 TEST64rr TEST8i8 TEST8mi TEST8ri TEST8rr\n    UCOMISDrr\n    VADDSDrm_Int VADDSDrr_Int\n    VANDPDrr\n    VCOMISDrr\n    VFMADD132SDm_Int VFMADD132SDr_Int VFMADD213SDm_Int VFMADD231SDr_Int\n    VFNMADD231SDm_Int\n    VMOVAPDrr\n    VMOVPDI2DIrr\n    VMOVPQIto64rr\n    VMOVQI2PQIrm\n    VMOVSDrm\n    VMULSDrm_Int VMULSDrr_Int\n    VSTMXCSR\n    VSUBSDrm_Int VSUBSDrr_Int\n    VUCOMISDrm VUCOMISDrr\n    VXORPDrr\n    XADD32rm\n    XCHG32rm XCHG64rr\n    XOR32ri XOR32ri8 XOR32rm XOR32rr XOR64rm XOR64rr XOR8rm\n    XORPSrr\n\nIntel x86\n---------\n\nThe x86 support is based on x86_64 and has the same limitations. However, its integration is more recent than X86_64\nand has less been tested.\n\nInstruction Coverage\n^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: bash\n\n    ABS_F\n    ADC32mi8 ADC32mr ADC32ri ADC32ri8 ADC32rm ADC32rr\n    ADD16mi8 ADD16mr ADD16ri ADD16rm ADD32i32 ADD32mi ADD32mi8 ADD32mr ADD32ri ADD32ri8 ADD32rm ADD32rr ADD8rr\n    ADD_F32m ADD_FPrST0 ADD_FrST0\n    AESENCLASTrr AESENCrr\n    AND16mi AND16mr AND32i32 AND32mi8 AND32mr AND32ri AND32ri8 AND32rm AND32rr AND8mi AND8mr AND8ri AND8rm AND8rr\n    BSWAP32r\n    BT32rr\n    CALL32m CALL32r CALLpcrel32\n    CDQ\n    CHS_F\n    CLD\n    CMOV32rm CMOV32rr\n    CMOVE_F\n    CMP16mi8 CMP16mr CMP16rm CMP32i32 CMP32mi CMP32mi8 CMP32mr CMP32ri CMP32ri8 CMP32rm CMP32rr CMP8i8 CMP8mi CMP8mr CMP8ri CMP8rm CMP8rr\n    CMPSB CMPSW\n    CMPXCHG32rm\n    COM_FIPr\n    COM_FIr\n    DEC32r_alt\n    DIV32m DIV32r\n    DIVR_F32m DIVR_F64m\n    DIV_F32m DIV_F64m\n    DIV_FPrST0\n    FCOMP64m\n    FLDCW16m\n    FLDENVm\n    FNSTCW16m FNSTSW16r\n    FRNDINT\n    FSTENVm\n    FXAM\n    IDIV32m\n    ILD_F32m ILD_F64m\n    IMUL32r IMUL32rm IMUL32rmi8 IMUL32rr IMUL32rri\n    INC32r_alt\n    IST_FP32m IST_FP64m\n    JCC_1 JCC_4\n    JMP32m JMP32r\n    JMP_1 JMP_4\n    LD_F0 LD_F1 LD_F32m LD_F64m LD_F80m LD_Frr\n    LEA32r\n    MOV16mi MOV16mr MOV16rm MOV32ao32 MOV32mi MOV32mr MOV32o32a MOV32ri MOV32rm MOV32rr MOV8mi MOV8mr MOV8rm MOV8rr\n    MOVAPSrr\n    MOVDQArm MOVDQArr\n    MOVDQUmr\n    MOVSB MOVSL\n    MOVSX32rm8 MOVSX32rr16 MOVSX32rr8\n    MOVUPSmr MOVUPSrm\n    MOVZX32rm16 MOVZX32rm8 MOVZX32rr16 MOVZX32rr8\n    MUL32m MUL32r\n    MUL_F32m MUL_FPrST0 MUL_FST0r\n    NEG32r\n    NOOP\n    NOT32m NOT32r\n    OR16ri OR16rm OR32i32 OR32mi OR32mi8 OR32mr OR32ri OR32ri8 OR32rm OR32rr OR8i8 OR8mi OR8mr OR8ri OR8rm OR8rr\n    POP32r\n    PSHUFBrr PSHUFDri\n    PSLLDQri PSLLDri\n    PUSH32i8 PUSH32r PUSH32rmm PUSHi32\n    PXORrr\n    RETL\n    ROL32r1 ROL32rCL ROL32ri\n    ROR32ri\n    SAHF\n    SAR32r1 SAR32rCL SAR32ri\n    SBB32mi8 SBB32ri8 SBB32rm SBB32rr SBB8i8 SBB8ri\n    SCASB\n    SETCCm SETCCr\n    SHL32rCL SHL32ri\n    SHLD32rrCL SHLD32rri8\n    SHR16ri SHR32r1 SHR32rCL SHR32ri SHR8m1 SHR8ri\n    SHRD32rri8\n    STOSL\n    ST_F64m ST_FP64m ST_FP80m ST_FPrr\n    SUB32i32 SUB32mi8 SUB32mr SUB32ri SUB32ri8 SUB32rm SUB32rr SUB8mr SUB8ri SUB8rm\n    SUBR_F64m SUBR_FPrST0\n    SUB_FPrST0 SUB_FrST0\n    TEST16mi TEST16ri TEST16rr TEST32i32 TEST32mi TEST32mr TEST32ri TEST32rr TEST8i8 TEST8mi TEST8mr TEST8ri TEST8rr\n    UCOM_FIr UCOM_FPr\n    XADD32rm\n    XCHG32ar XCHG32rm XCHG32rr\n    XCH_F\n    XOR16rr XOR32i32 XOR32mr XOR32ri XOR32ri8 XOR32rm XOR32rr XOR8rm\n    XORPSrr\n\n"
  },
  {
    "path": "docs/source/changelog.rst",
    "content": "CHANGELOG\n=========\n\nNext Release (0.12.2)\n---------------------\n\n\nVersion (0.12.1)\n----------------\n\n2026-01-07 QBDI Team <qbdi@quarkslab.com>\n\n* Support for Arm64e on OSX (`#310 <https://github.com/QBDI/QBDI/pull/310>`_)\n* Fix cross-compiling from windows to android (`#315 <https://github.com/QBDI/QBDI/pull/315>`_, `#316 <https://github.com/QBDI/QBDI/pull/316>`_ and `#318 <https://github.com/QBDI/QBDI/pull/318>`_)\n* Fix broken support of some AArch64 instructions (`#313 <https://github.com/QBDI/QBDI/pull/313>`_ and `#320 <https://github.com/QBDI/QBDI/pull/320>`_)\n\nVersion (0.12.0)\n----------------\n\n2025-10-14 QBDI Team <qbdi@quarkslab.com>\n\n* Add QBDIPreload implementation for windows (`#110 <https://github.com/QBDI/QBDI/pull/110>`_, `#227 <https://github.com/QBDI/QBDI/pull/227>`_ and `#277 <https://github.com/QBDI/QBDI/pull/277>`_)\n* Experimental support for IOS AArch64\n  (`#300 <https://github.com/QBDI/QBDI/pull/300>`_ and `#303 <https://github.com/QBDI/QBDI/pull/303>`_).\n  This support is experimental and work only on some jailbroken device.\n* Add new user API ``QBDI::VM::getNbExecBlock`` and ``QBDI::VM::reduceCacheTo`` to control the size of QBDI cache (`#277 <https://github.com/QBDI/QBDI/pull/277>`_)\n* Add new user API ``QBDI::VM::getJITInstAnalysis`` and new analysis\n  ``QBDI::AnalysisType::ANALYSIS_JIT`` to provide JIT information (`#277 <https://github.com/QBDI/QBDI/pull/277>`_)\n* Support legacy instruction JCXZ/JECXZ/JRCXZ on X86 and X86_64 (`#289 <https://github.com/QBDI/QBDI/pull/289>`_)\n* Add error message when PyQBDI preload fail (`#286 <https://github.com/QBDI/QBDI/pull/286>`_)\n* Update Frida API (`#290 <https://github.com/QBDI/QBDI/pull/290>`_, `#266 <https://github.com/QBDI/QBDI/pull/266>`_ and `#282 <https://github.com/QBDI/QBDI/pull/282>`_)\n* Update to LLVM19 (`#271 <https://github.com/QBDI/QBDI/pull/271>`_)\n* Support python 3.13 and 3.14 (`#270 <https://github.com/QBDI/QBDI/pull/270>`_ and `#302 <https://github.com/QBDI/QBDI/pull/302>`_)\n* Fix missing header (`#292 <https://github.com/QBDI/QBDI/pull/292>`_)\n* Fix bugeous initialisation of FTW register on windows (`#299 <https://github.com/QBDI/QBDI/pull/299>`_)\n* Fix docs typos (`#265 <https://github.com/QBDI/QBDI/pull/265>`_)\n* Fix immediate representation in disassembly (`#295 <https://github.com/QBDI/QBDI/pull/295>`_ and `#296 <https://github.com/QBDI/QBDI/pull/296>`_)\n* Fix wrong number of operand in LLVM::MCInst (`#291 <https://github.com/QBDI/QBDI/pull/291>`_ and `#304 <https://github.com/QBDI/QBDI/pull/304>`_)\n* Update template with new name from 0.11.0 (`#259 <https://github.com/QBDI/QBDI/pull/259>`_)\n\nVersion (0.11.0)\n----------------\n\n2024-05-17 QBDI Team <qbdi@quarkslab.com>\n\n* Fix ARM instrumentation for 'mov pc, lr' (`#241 <https://github.com/QBDI/QBDI/pull/241>`__)\n* Add switchStackAndCall API (`#245 <https://github.com/QBDI/QBDI/pull/245>`__)\n* Rename ``QBDI::InstAnalysis::module`` and ``QBDI::InstAnalysis::symbol`` to\n  ``QBDI::InstAnalysis::moduleName`` and ``QBDI::InstAnalysis::symbolName``. The\n  same changed applied in C, C++, Python and JS API, but Python and JS API\n  deprecated but still support the previous name.\n* Update LLVM to LLVM17 (`#253 <https://github.com/QBDI/QBDI/pull/253>`_)\n* Support copy and pickle for GPRState and FPRState in PyQBDI (`#247 <https://github.com/QBDI/QBDI/pull/247>`_, `#248 <https://github.com/QBDI/QBDI/pull/248>`_)\n* Support python 3.12 (`#247 <https://github.com/QBDI/QBDI/pull/247>`_)\n\nVersion 0.10.0\n--------------\n\n2023-01-26 QBDI Team <qbdi@quarkslab.com>\n\n* Fix Ubuntu package (`#217 <https://github.com/QBDI/QBDI/pull/217>`__)\n* Support ARMv7 and AArch64 architecture (`#222 <https://github.com/QBDI/QBDI/pull/222>`__)\n* Support python 3.11 (`#222 <https://github.com/QBDI/QBDI/pull/222>`__)\n* Support Frida >= 15.2 (`#222 <https://github.com/QBDI/QBDI/pull/222>`__ and `#223 <https://github.com/QBDI/QBDI/pull/223>`_)\n\nInternal update:\n\n* Move windows CI to Github Actions (`#222 <https://github.com/QBDI/QBDI/pull/222>`__)\n* Support python build with ``pyproject.toml`` (`#222 <https://github.com/QBDI/QBDI/pull/222>`__)\n* Update LLVM to LLVM15 (`#224 <https://github.com/QBDI/QBDI/pull/224>`_)\n* Add CI for ARMv7 and AArch64 (`#222 <https://github.com/QBDI/QBDI/pull/222>`__ and `#225 <https://github.com/QBDI/QBDI/pull/225>`_)\n\nVersion 0.9.0\n-------------\n\n2022-03-31 QBDI Team <qbdi@quarkslab.com>\n\n* Change internal log system (`#174 <https://github.com/QBDI/QBDI/pull/174>`_).\n\n  * The API ``QBDI::addLogFilter`` has been replaced by :cpp:func:`QBDI::setLogPriority`.\n  * The API ``QBDI::setLogOutput`` has been replaced by\n    :cpp:func:`QBDI::setLogFile`, :cpp:func:`QBDI::setLogConsole` and\n    :cpp:func:`QBDI::setLogDefault`.\n\n* Fix templates (`#186 <https://github.com/QBDI/QBDI/pull/186>`_)\n* Fix Frida-QBDI for Frida 15.0.0 (`#192 <https://github.com/QBDI/QBDI/pull/192>`_)\n* Change behavior of :cpp:func:`QBDI::VM::addInstrumentedModuleFromAddr` to work\n  with mmap region (`#193 <https://github.com/QBDI/QBDI/pull/193>`_)\n* Add Priority to InstCallback API (`#194 <https://github.com/QBDI/QBDI/pull/194>`_).\n  If two or more InstCallback target the same position (PRE or POST) of the same\n  instruction, the priority parameter allows to specify which InstCallback should\n  be called first. see :cpp:enum:`QBDI::CallbackPriority`\n* Support for X86 ``loop``, ``loope`` and ``loopne`` instructions\n  (`#200 <https://github.com/QBDI/QBDI/pull/200>`_)\n* Add support for ``FS`` and ``GS`` segment in X86_64\n  (`#190 <https://github.com/QBDI/QBDI/pull/190>`_). To support the feature, the\n  kernel must support ``RDFSBASE``, ``RDGSBASE``, ``WRFSBASE`` and ``WRGSBASE``\n  instructions (linux >= 5.9). To enable the support, the option\n  :cpp:enumerator:`QBDI::Options::OPT_ENABLE_FS_GS` must be enabled.\n* Hide LLVM symbols from shared library and QBDIPreload\n  (`#205 <https://github.com/QBDI/QBDI/pull/205>`_)\n* Support python 3.10 for PyQBDI\n  (`#206 <https://github.com/QBDI/QBDI/pull/206>`_)\n* Add VMAction :cpp:enumerator:`QBDI::VMAction::SKIP_INST` and\n  :cpp:enumerator:`QBDI::VMAction::SKIP_PATCH`\n  (`#197 <https://github.com/QBDI/QBDI/pull/197>`_)\n\n  * :cpp:enumerator:`QBDI::VMAction::SKIP_INST` can be used to emulate the\n    instruction with a PREINST callback. When this VMAction is returned by a\n    PREINST callback, QBDI will directly jump to the POSTINST callback.\n  * :cpp:enumerator:`QBDI::VMAction::SKIP_PATCH` can be used to jump over all\n    the reminding callback for the current instruction. If uses in PREINST\n    position, the instruction will not be executed.\n\n  The value associated with the existing :cpp:enum:`QBDI::VMAction` has changed.\n\n* Add tutorial for basic block VMEvent (`#165 <https://github.com/QBDI/QBDI/pull/165>`_)\n* Support C++ lambda with capture. (`#207 <https://github.com/QBDI/QBDI/pull/207>`_)\n  see :cpp:type:`QBDI::InstCbLambda`, :cpp:type:`QBDI::VMCbLambda`\n  and :cpp:type:`QBDI::InstrRuleCbLambda`\n* Fix a bug where some symbols were missing in QBDIPreload (`#209 <https://github.com/QBDI/QBDI/pull/209>`_)\n* Remove new name of libc in QBDIPreload (`#211 <https://github.com/QBDI/QBDI/pull/211>`_)\n* Support of some self-modifying code (`#212 <https://github.com/QBDI/QBDI/pull/212>`_).\n  QBDI will not crash if invalid instructions are found when handling a new basic\n  block.\n* Add tutorial for ExecBroker VMEvent (`#166 <https://github.com/QBDI/QBDI/pull/166>`_)\n* Change :cpp:func:`QBDI::getVersion` out parameter to return version to the form ``0xMMmmpp`` (`#214 <https://github.com/QBDI/QBDI/pull/214>`_)\n\nInternal update:\n\n* Add static library licenses in LICENSE.txt (`#169 <https://github.com/QBDI/QBDI/pull/169>`_)\n* Format code with clang-format and cmake-format (`#175 <https://github.com/QBDI/QBDI/pull/175>`_)\n* Fix integer overflow in cache optimisation (`#168 <https://github.com/QBDI/QBDI/pull/168>`_)\n* Refactor build system, add llvm build in main cmake with FetchContent,\n  move all LLVM object in a new LLVMCPU class, split internal class, split state by architecture\n  (`#178 <https://github.com/QBDI/QBDI/pull/178>`_,\n  `#179 <https://github.com/QBDI/QBDI/pull/179>`_ and\n  `#188 <https://github.com/QBDI/QBDI/pull/188>`_)\n* Update LLVM to LLVM 13.0.0, remove zlib dependency\n  (`#180 <https://github.com/QBDI/QBDI/pull/189>`_, `#196 <https://github.com/QBDI/QBDI/pull/196>`_)\n* Remove empty Patch not associated to an MCInst (`#195 <https://github.com/QBDI/QBDI/pull/195>`_)\n* Compile assembly with ``--noexecstack`` to have a ``rw-`` stack when using QBDI\n  on linux (`#201 <https://github.com/QBDI/QBDI/pull/201>`_)\n* Use build directory to build the documentation (`#213 <https://github.com/QBDI/QBDI/pull/213>`_)\n* Use Doxygen 1.9.2 in readthedocs (`#214 <https://github.com/QBDI/QBDI/pull/214>`_)\n\n\nVersion 0.8.0\n-------------\n\n2021-02-11 QBDI Team <qbdi@quarkslab.com>\n\n* Fix android compilation (`#126 <https://github.com/QBDI/QBDI/pull/126>`_)\n* Fix instrumentation of Pusha and Popa on X86 (`#127 <https://github.com/QBDI/QBDI/pull/127>`_)\n* Fix getBBMemoryAccess (`#128 <https://github.com/QBDI/QBDI/pull/128>`_)\n\n  * Improve the documentation of getBBMemoryAccess\n  * Add recordMemoryAccess callback before any InstCallback\n\n* Refactor ExecBlockManager to work with unaligned instruction on X86 and X86-64 (`#129 <https://github.com/QBDI/QBDI/pull/129>`_)\n* Drop early support for ARM. The support hasn't been tested since 0.6.2.\n* Rework cmake package export to import X86 and X86_64 version of QBDI in one CMake (`#146 <https://github.com/QBDI/QBDI/pull/146>`_ and `#132 <https://github.com/QBDI/QBDI/pull/132>`_)\n* Add :cpp:func:`QBDI::VM::getCachedInstAnalysis` to retrieve an InstAnalysis from an address. The address must be cached in the VM. (`#148 <https://github.com/QBDI/QBDI/pull/148>`_)\n* Change in ``InstAnalysis`` and ``OperandAnalysis`` (`#153 <https://github.com/QBDI/QBDI/pull/153>`_):\n\n  * Add ``InstAnalysis.flagsAccess`` to determine if the instruction uses or sets the flags (``EFLAGS`` register). The analysis ``ANALYSIS_OPERANDS`` is needed to use this field.\n  * Change ``InstAnalysis.mayLoad`` and ``InstAnalysis.mayStore`` definition. The field will be true if QBDI detects memory access for the instruction.\n  * Add ``InstAnalysis.loadSize`` and ``InstAnalysis.storeSize``. If the instruction will read or write the memory, the expected size of the access is given by these fields.\n    The analysis ``ANALYSIS_INSTRUCTION`` is needed to use this field.\n  * Add ``InstAnalysis.condition``. With the update of LLVM, the mnemonic for conditional jump (like ``JE_4``) are merged in a unique mnemonic ``JCC_4``.\n    This new field will contain the condition.\n    The analysis ``ANALYSIS_INSTRUCTION`` is needed to use this field. A new enum ``ConditionType`` has all the possible value.\n  * Add ``OPERANDFLAG_IMPLICIT`` for ``OperandAnalysis.flag``. An operand will have this flag when a register is implicit to the instruction.\n  * Add ``OPERAND_FPR`` for ``OperandAnalysis.type``. This type is used for floating point registers.\n    For this type, ``OperandAnalysis.regCtxIdx`` is the offset in ``FPRState`` or -1 when an offset cannot be provided.\n  * Add ``OPERAND_SEG`` for ``OperandAnalysis.type``. This type is used for segments or other unsupported register (like ``SSP``).\n  * Change type of ``OperandAnalysis.regCtxIdx`` to signed integer. When the value is less than 0, the index is invalid.\n  * Change algorithm for ``OperandAnalysis``. The type ``OPERAND_INVALID`` may be present in the list of operands when a register is unset with the current instruction.\n    Many operands may describe the used of the same register when a register is used multiple times for different purposes by the instruction.\n\n* Add Instrumentation rule callback :c:type:`QBDI_InstrRuleDataCBK` and :cpp:type:`QBDI::InstrRuleDataCBK` (`#151 <https://github.com/QBDI/QBDI/pull/151>`_)\n\n  The Instrumentation rule callback receives an InstAnalysis of each instruction during the instrumentation process. Based on this analysis, the callback\n  may insert custom InstCallback for each instruction.\n\n  The call order of the callback has changed for the PREINST callback. If an instruction has multiple callbacks in PREINST position, they will be called\n  in the reverse order of registration.\n\n* Support SIMD MemoryAccess and change :cpp:struct:`QBDI::MemoryAccess` structure (`#154 <https://github.com/QBDI/QBDI/pull/154>`_)\n\n  * Add :cpp:member:`QBDI::MemoryAccess::flags`. In some cases, QBDI cannot provide all information about the access. This field\n    describes the limitation for each access. Three limitations may be reached:\n\n    * :cpp:enumerator:`QBDI::MemoryAccessFlags::MEMORY_UNKNOWN_SIZE`: the size of the access isn't known. Only the address is valid.\n      The flag is only set for instruction with REP prefix before the execution of the instruction.\n    * :cpp:enumerator:`QBDI::MemoryAccessFlags::MEMORY_MINIMUM_SIZE`: the size isn't the real size of the access, but the expected minimal size.\n      This flag is used for instruction with complex access like ``XSAVE`` and ``XRSTOR``.\n    * :cpp:enumerator:`QBDI::MemoryAccessFlags::MEMORY_UNKNOWN_VALUE`: the value of the access hasn't been saved.\n      The more common reason is that the access size is greater than the size of :cpp:member:`QBDI::MemoryAccess::value`.\n      This flag is also used for instruction with REP prefix when the access size cannot be determined during the instrumentation.\n\n  * Fix MemoryAccess for some generic instruction.\n\n* Add VM Options. (`#144 <https://github.com/QBDI/QBDI/pull/144>`_)\n\n  Some options can be provided to the VM to enable or disable some features:\n\n  * :cpp:enumerator:`QBDI::Options::OPT_DISABLE_FPR`: Disable FPRState backup and restore in context switches.\n    Only the GPRState will be used.\n  * :cpp:enumerator:`QBDI::Options::OPT_DISABLE_OPTIONAL_FPR`: When :cpp:enumerator:`QBDI::Options::OPT_DISABLE_FPR` isn't selected,\n    QBDI will detect if a BasicBlock needs FPRState. When BasicBlock doesn't need FPRState, the state will not be restored.\n    This option forces the restoration and backup of FPRState to every BasicBlock.\n  * :cpp:enumerator:`QBDI::Options::OPT_ATT_SYNTAX` for X86 and X86_64: :cpp:member:`QBDI::InstAnalysis::disassembly` will be in\n    AT&T syntax instead of Intel Syntax.\n\n* Rework documentation (`#156 <https://github.com/QBDI/QBDI/pull/156>`_)\n\nInternal update:\n\n* Update LLVM to LLVM 10.0.1 (`#104 <https://github.com/QBDI/QBDI/pull/104>`_ and `#139 <https://github.com/QBDI/QBDI/pull/139>`_)\n* Reduce LLVM library included in QBDI static library and reduce QBDI package size (`#139 <https://github.com/QBDI/QBDI/pull/139>`_ and `#70 <https://github.com/QBDI/QBDI/issues/70>`_)\n* Replace GTest by `Catch2 <https://github.com/catchorg/Catch2>`_ (`#140 <https://github.com/QBDI/QBDI/pull/140>`_)\n* Refactor code and switch to cpp17 (`#140 <https://github.com/QBDI/QBDI/pull/140>`_ and `#155 <https://github.com/QBDI/QBDI/pull/155>`_)\n* Use Github Actions to build dev-next package of QBDI (linux, osx and android) and PyQBDI (linux and osx) (`#147 <https://github.com/QBDI/QBDI/pull/147>`_, `#159 <https://github.com/QBDI/QBDI/pull/159>`_)\n* Rewrite frida-qbdi.js and use sphinx-js for frida-QBDI documentation (`#146 <https://github.com/QBDI/QBDI/pull/146>`_).\n  A version of frida greater or equals to 14.0 is needed to run frida-qbdi.js (need support of ES2019).\n* Refactor MemoryAccess Code and add new tests (`#154 <https://github.com/QBDI/QBDI/pull/154>`_)\n* Handle VMCallback return value (`#155 <https://github.com/QBDI/QBDI/pull/155>`_)\n* Optimize Context Switch and FPRState restoration (`#144 <https://github.com/QBDI/QBDI/pull/144>`_)\n* Add commit hash in devel version (`#158 <https://github.com/QBDI/QBDI/pull/158>`_)\n\nVersion 0.7.1\n-------------\n\n2020-02-27 QBDI Team <qbdi@quarkslab.com>\n\n* Refactor PyQBDI, support python3, PyQBDI without Preload (`#67 <https://github.com/QBDI/QBDI/issues/67>`_,\n  `#121 <https://github.com/QBDI/QBDI/pull/121>`_)\n* Remove ncurses dependency (`#123 <https://github.com/QBDI/QBDI/pull/123>`_)\n* Fix initFPRState (`#114 <https://github.com/QBDI/QBDI/issues/114>`_)\n\n\nVersion 0.7.0\n-------------\n\n2019-09-10 QBDI Team <qbdi@quarkslab.com>\n\n* Add support for the x86 architecture\n* Add new platforms related to Android: android-X86 and android-X86_64\n* Improve :c:type:`MemoryMap` structure by adding the module's full path if available\n  (`#62 <https://github.com/QBDI/QBDI/issues/62>`_, `#71 <https://github.com/QBDI/QBDI/issues/71>`_)\n* Create docker images for QBDI (available on DockerHub `qbdi/qbdi <https://hub.docker.com/r/qbdi/qbdi>`_)\n  (`#56 <https://github.com/QBDI/QBDI/pull/56>`_)\n* Fix and improve operands analysis involved in memory accesses (`#58 <https://github.com/QBDI/QBDI/issues/58>`_) :\n\n  In the previous version, the output of the instruction analysis for **some** instructions did not contain the information\n  related to memory accesses.\n\n  For instance, the *operand analysis* of ``cmp MEM, IMM`` misses information about the first operand:\n\n  .. code:: text\n\n      cmp dword ptr [rbp + 4 * rbx - 4], 12345678\n          [0] optype: 1, value : 12345678, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n\n  This issue has been fixed and the :c:type:`OperandAnalysis` structure contains a new  attribute: ``flag``,\n  which is used to distinct :c:type:`OperandAnalysis` involved in memory accesses from the others.\n\n  Here is an example of output:\n\n  .. code:: text\n\n      cmp dword ptr [rbp + 4*rbx - 4], 12345678\n          [0] optype: 2, flag: 1, value : 48, size: 8, regOff: 0, regCtxIdx: 14, regName: RBP, regaccess : 1\n          [1] optype: 1, flag: 1, value : 4, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n          [2] optype: 2, flag: 1, value : 49, size: 8, regOff: 0, regCtxIdx: 1, regName: RBX, regaccess : 1\n          [3] optype: 1, flag: 1, value : -4, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n          [4] optype: 1, flag: 0, value : 12345678, size: 4, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n      mov rax, qword ptr [rbp - 4]\n          [0] optype: 2, flag: 0, value : 47, size: 8, regOff: 0, regCtxIdx: 0, regName: RAX, regaccess : 2\n          [1] optype: 2, flag: 1, value : 48, size: 8, regOff: 0, regCtxIdx: 14, regName: RBP, regaccess : 1\n          [2] optype: 1, flag: 1, value : 1, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n          [3] optype: 1, flag: 1, value : -4, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n      mov rax, qword ptr [4*rbx]\n          [0] optype: 2, flag: 0, value : 47, size: 8, regOff: 0, regCtxIdx: 0, regName: RAX, regaccess : 2\n          [1] optype: 1, flag: 1, value : 4, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n          [2] optype: 2, flag: 1, value : 49, size: 8, regOff: 0, regCtxIdx: 1, regName: RBX, regaccess : 1\n          [3] optype: 1, flag: 1, value : 0, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n      jne -6115\n          [0] optype: 1, flag: 2, value : -6115, size: 4, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n      lea rax, [rbp + 4*rbx - 4]\n          [0] optype: 2, flag: 0, value : 47, size: 8, regOff: 0, regCtxIdx: 0, regName: RAX, regaccess : 2\n          [1] optype: 2, flag: 4, value : 48, size: 8, regOff: 0, regCtxIdx: 14, regName: RBP, regaccess : 1\n          [2] optype: 1, flag: 4, value : 4, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n          [3] optype: 2, flag: 4, value : 49, size: 8, regOff: 0, regCtxIdx: 1, regName: RBX, regaccess : 1\n          [4] optype: 1, flag: 4, value : -4, size: 8, regOff: 0, regCtxIdx: 0, regName: (null), regaccess : 0\n\n\nVersion 0.6.2\n-------------\n\n2018-10-19 Cedric TESSIER <ctessier@quarkslab.com>\n\n* Add support for a public CI (based on Travis and AppVeyor)\n* Fix instruction operands analysis (`#57 <https://github.com/QBDI/QBDI/issues/57>`_,\n  `#59 <https://github.com/QBDI/QBDI/pull/59>`_)\n* Add missing MEMORY_READ enum value in Python bindings (`#61 <https://github.com/QBDI/QBDI/issues/61>`_)\n* Fix cache misbehavior on corner cases (`#49 <https://github.com/QBDI/QBDI/issues/49>`_,\n  `#51 <https://github.com/QBDI/QBDI/pull/51>`_)\n* Add missing memory access instructions on x86_64 (`#45 <https://github.com/QBDI/QBDI/issues/45>`_,\n  `#47 <https://github.com/QBDI/QBDI/issues/47>`_, `#72 <https://github.com/QBDI/QBDI/pull/72>`_)\n* Enable asserts in Debug builds (`#48 <https://github.com/QBDI/QBDI/issues/48>`_)\n\nVersion 0.6.1\n-------------\n\n2018-03-22 Charles HUBAIN <chubain@quarkslab.com>\n\n* Fixing a performance regression with the addCodeAddrCB (`#42 <https://github.com/QBDI/QBDI/issues/42>`_):\n\n  Since 0.6, this API would trigger a complete cache flush forcing the engine to regenerate\n  all the instrumented code after each call. Since this API is used inside VM:run(), this\n  had the effect of completely canceling precaching optimization where used.\n\n* Fixing support for AVX host without AVX2 support (`#19 <https://github.com/QBDI/QBDI/issues/19>`_):\n\n  Context switching was wrongly using AVX2 instructions instead of AVX instructions causing\n  segfaults under hosts supporting AVX but not AVX2.\n\nVersion 0.6\n-----------\n\n2018-03-02 Charles HUBAIN <chubain@quarkslab.com>\n\n* Important performance improvement in the core engine (`#30 <https://github.com/QBDI/QBDI/pull/30>`_)\n  **This slightly changes the behavior of VMEvents.**\n* Fix the addCodeAddrCB API (`#37 <https://github.com/QBDI/QBDI/pull/37>`_)\n* atexit and getCurrentProcessMap in python bindings (`#35 <https://github.com/QBDI/QBDI/pull/35>`_)\n* Fix getInstAnalysis on BASIC_BLOCK_ENTRY (`#28 <https://github.com/QBDI/QBDI/issues/28>`_)\n* Various documentation improvements (`#34 <https://github.com/QBDI/QBDI/pull/34>`_,\n  `#37 <https://github.com/QBDI/QBDI/pull/37>`_, `#38 <https://github.com/QBDI/QBDI/pull/38>`_,\n  `#40 <https://github.com/QBDI/QBDI/pull/40>`_)\n  and an API uniformisation (`#29 <https://github.com/QBDI/QBDI/issues/29>`_)\n\nVersion 0.5\n-----------\n\n2017-12-22 Cedric TESSIER <ctessier@quarkslab.com>\n\n* Official public release!\n\nVersion 0.5 RC3\n---------------\n\n2017-12-10 Cedric TESSIER <ctessier@quarkslab.com>\n\n* Introducing pyqbdi, full featured python bindings based on QBDIPreload library\n* Revising variadic API to include more friendly prototypes\n* Various bug, compilation and documentation fixes\n\nVersion 0.5 RC2\n---------------\n\n2017-10-30 Charles HUBAIN <chubain@quarkslab.com>\n\n* Apache 2 licensing\n* New QBDIPreload library for easier dynamic injection under linux and macOS\n* Various bug, compilation and documentation fixes\n* Big tree cleanup\n\nVersion 0.5 RC1\n---------------\n\n2017-10-09 Charles HUBAIN <chubain@quarkslab.com>\n\n* New Frida bindings\n* Upgrade to LLVM 5.0\n* Support for AVX registers\n* New callback helpers on mnemonics and memory accesses\n* Basic block precaching API\n* Automatic cache invalidation when a new instrumentation is added\n* Instruction and sequence level cache avoids needless retranslation\n* Upgrade of the validator which now supports Linux and macOS\n\nVersion 0.4\n-----------\n\n2017-01-06 Charles HUBAIN <chubain@quarkslab.com>\n\n* Basic Instruction Shadows concept\n* Memory access PatchDSL statements with support under X86_64 (non SIMD memory access only)\n* Shadow based memory access API and instrumentation\n* C and C++ API stabilization\n* Out-of-tree build and SDK\n* Overhaul of the entire documentation with a complete PatchDSL explanation and a split\n  between user and developer documentation.\n\nVersion 0.3\n-----------\n\n2016-04-29 Charles HUBAIN <chubain@quarkslab.com>\n\n* Partial ARM support, sufficient to run simple program e.g cat, ls, ...\n* Instrumentation filtering system, ExecBroker, allowing the engine to switch between non\n  instrumented and instrumented execution\n* Complex execution validation system under linux which allows to do instruction per instruction\n  compared execution between a non instrumented and an instrumented instance of a program\n* New callback system for Engine related event e.g basic block entry / exit, ExecBroker\n  transfer / return.\n* New (internal) logging system, LogSys, which allows to do priority and tag based filtering of the debug logs.\n\nVersion 0.2\n-----------\n\n2016-01-29 Charles HUBAIN <chubain@quarkslab.com>\n\n* Upgrade to LLVM 3.7\n* Complete X86_64 patching support\n* Support of Windows X86_64\n* Basic callback based instrumentation\n* Usable C++ and C API\n* User documentation with examples\n* Uniformisation of PatchDSL\n\nVersion 0.1\n-----------\n\n2015-10-09 Charles HUBAIN <chubain@quarkslab.com>\n\n* Ported the PatchDSL from the minijit PoC\n* Corrected several design flaws in the PatchDSL\n* Implemented a comparated execution test setup to prove the execution via the JIT yields the\n  same registers and stack state as a normal execution\n* Basic patching working for ARM and X86_64 architectures as shown by the compared execution\n  tests\n\nVersion 0.0\n-----------\n\n2015-09-17 Charles HUBAIN <chubain@quarkslab.com>\n\n* Working dependency system for LLVM and Google Test\n* ExecBlock working and tested on linux-X86_64, linux-ARM, android-ARM and macOS-X86_64\n* Deployed buildbot infrastructure for automated build and test on linux-X86_64 and linux-ARM\n"
  },
  {
    "path": "docs/source/compilation.rst",
    "content": "\nCompilation From Source\n=======================\n\n.. include:: ../../README.rst\n    :start-after: .. compil\n    :end-before: .. compil-end\n\nCMake Parameters\n++++++++++++++++\n\nThe compilation of QBDI can be configured with the command line. Each parameter\nshould be placed on the command line with the form ``-D<param>=<value>``.\n\n* ``QBDI_PLATFORM`` (mandatory) : Target platform of the compilation\n  (supported: ``windows``, ``linux``, ``android``, ``macos``)\n* ``QBDI_ARCH`` (mandatory) : Target architecture of the compilation\n  (supported: ``X86_64``, ``X86``)\n* ``QBDI_CCACHE`` (default ON) : enable compilation optimisation with ccache or sccache.\n* ``QBDI_DISABLE_AVX`` (default OFF) : disable the support of AVX instruction\n  on X86 and X86_64\n* ``QBDI_ASAN`` (default OFF) : compile with ASAN to detect memory leak in QBDI.\n* ``QBDI_LOG_DEBUG`` (default OFF) : enable the debug level of the logging\n  system. Note that the support of this level has an impact on the performances,\n  even if this log level is not enabled.\n* ``QBDI_STATIC_LIBRARY`` (default ON) : build the static library of QBDI. Note\n  than some subproject need ``QBDI_STATIC_LIBRARY`` (test, PyQBDI, ...)\n* ``QBDI_SHARED_LIBRARY`` (default ON) : build the shared library of QBDI. Note\n  than some subproject need ``QBDI_SHARED_LIBRARY`` (Frida/QBDI, examples, ...)\n* ``QBDI_TEST`` (default ON) : build the tests suite\n* ``QBDI_BENCHMARK`` (default OFF) : build the benchmark tools\n* ``QBDI_TOOLS_QBDIPRELOAD`` (default ON on supported platform) : build\n  QBDIPreload static library.\n* ``QBDI_TOOLS_VALIDATOR`` (default ON on supported platform) : build\n  the validator library (supported on Linux and macOS).\n* ``QBDI_TOOLS_PYQBDI`` (default ON on X86_64) : build PyQBDI library.\n  Supported on Linux, Windows and macOS.\n* ``QBDI_TOOLS_FRIDAQBDI`` (default ON) : add Frida/QBDI in the package.\n\n\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# QBDI documentation build configuration file, created by\n# sphinx-quickstart on Mon Nov  2 10:36:40 2015.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys\nimport os\nimport subprocess\nimport re\n\nRE_VERSION = re.compile('.*QBDI_VERSION_(\\\\S*)\\\\s*(\\\\d*).*')\n\n# Parse version\ndef extract_version(cmakefile):\n    major = minor = patch = dev = -1\n    with open(cmakefile) as fp:\n        for line in fp.readlines():\n            m = RE_VERSION.match(line)\n            if not m:\n                continue\n            cat, version = m.groups()\n            if cat == 'MAJOR':\n                major = int (version)\n            elif cat == 'MINOR':\n                minor = int (version)\n            elif cat == 'PATCH':\n                patch = int (version)\n            elif cat == 'DEV':\n                dev = bool(int (version))\n            if major != -1 and minor != -1 and patch != -1 and dev != -1:\n                break\n    return (major, minor, patch, dev)\n\n# Extract version from CMakeLists.txt\n# We cannot use cmake configure_file because documentation is built without cmake on readthedocs\ncurrent_dir = os.path.dirname(os.path.realpath(__file__))\ncmake_path = os.path.join(current_dir, '..', '..', 'cmake', 'QBDIConfig.cmake')\nVERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_DEV = extract_version(cmake_path)\nVERSION_FULL = \"{}.{}.{}\".format(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)\nif VERSION_DEV:\n    VERSION_FULL = VERSION_FULL + '-devel'\n\n# Handle \"read the docs\" builds\nread_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'\n\nif read_the_docs_build:\n    doxygen_dirs = os.path.join(current_dir, '..')\n    # Update documentation in doxygen config file (what is normally done by cmake)\n    sed_cmd = [\n            's/@QBDI_VERSION_MAJOR@/{:d}/'.format(VERSION_MAJOR),\n            's/@QBDI_VERSION_MINOR@/{:d}/'.format(VERSION_MINOR),\n            's/@QBDI_VERSION_PATCH@/{:d}/'.format(VERSION_PATCH),\n            's/@QBDI_VERSION_DEV@/{:d}/'.format(VERSION_DEV),\n            's/@QBDI_VERSION_STRING@/{}/'.format(VERSION_FULL),\n            's/@CMAKE_CURRENT_BINARY_DIR@/./',\n            's/@CMAKE_CURRENT_SOURCE_DIR@/./',\n            's/@QBDI_ARCH@/X86_64/',\n            's/@QBDI_PLATFORM@/linux/',\n    ]\n    sedcmd = \"sed '{}'\".format(\";\".join(sed_cmd))\n    # Call doxygen\n    subprocess.call(\"{} qbdi_cpp.doxygen.in > qbdi_cpp.doxygen\".format(sedcmd), shell=True, cwd=doxygen_dirs)\n    subprocess.call(\"{} qbdi_c.doxygen.in > qbdi_c.doxygen\".format(sedcmd), shell=True, cwd=doxygen_dirs)\n    subprocess.call(\"{} qbdipreload.doxygen.in > qbdipreload.doxygen\".format(sedcmd), shell=True, cwd=doxygen_dirs)\n    subprocess.call(\"cp arch/X86_64/* .\", shell=True, cwd=os.path.join(current_dir, '..', '..', 'include', 'QBDI'))\n    subprocess.call(\"{} Version.h.in > Version.h\".format(sedcmd), shell=True, cwd=os.path.join(current_dir, '..', '..', 'include', 'QBDI'))\n    subprocess.call('doxygen --version', shell=True, cwd=doxygen_dirs)\n    subprocess.call('doxygen qbdi_cpp.doxygen', shell=True, cwd=doxygen_dirs)\n    subprocess.call('doxygen qbdi_c.doxygen', shell=True, cwd=doxygen_dirs)\n    subprocess.call('doxygen qbdipreload.doxygen', shell=True, cwd=doxygen_dirs)\nelse:\n    doxygen_dirs = os.environ.get(\"QBDI_DOXYGEN_DIRS\", os.path.join(current_dir, '..'))\n\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#sys.path.insert(0, os.path.abspath('.'))\nsys.path.insert(0, os.path.abspath('..'))\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = ['breathe', 'sphinx.ext.autodoc', 'sphinx_js']\nbreathe_default_project = \"QBDI_CPP\"\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'QBDI'\ncopyright = '2015-2025, Quarkslab'\nauthor = 'Quarkslab'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = VERSION_FULL\n# The full version, including alpha/beta/rc tags.\nrelease = VERSION_FULL\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = \"en\"\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = []\n\n# The reST default role (used for this markup: `text`) to use for all\n# documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n# add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n# If true, keep warnings as \"system message\" paragraphs in the built documents.\n#keep_warnings = False\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n#html_theme_path = []\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Add any extra paths that contain custom files (such as robots.txt or\n# .htaccess) here, relative to this directory. These files are copied\n# directly to the root of the documentation.\n#html_extra_path = []\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Language to be used for generating the HTML full-text search index.\n# Sphinx supports the following languages:\n#   'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'\n#   'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'\n#html_search_language = 'en'\n\n# A dictionary with options for the search language support, empty by default.\n# Now only 'ja' uses this config value\n#html_search_options = {'type': 'default'}\n\n# The name of a javascript file (relative to the configuration directory) that\n# implements a search results scorer. If empty, the default will be used.\n#html_search_scorer = 'scorer.js'\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'QBDIdoc'\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n# The paper size ('letterpaper' or 'a4paper').\n#'papersize': 'letterpaper',\n\n# The font size ('10pt', '11pt' or '12pt').\n#'pointsize': '10pt',\n\n# Additional stuff for the LaTeX preamble.\n#'preamble': '',\n\n# Latex figure (float) alignment\n#'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n  (master_doc, 'QBDI.tex', 'QBDI Documentation',\n   'Quarkslab', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'qbdi', 'QBDI Documentation',\n     [author], 1)\n]\n\n# If true, show URL addresses after external links.\n#man_show_urls = False\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n  (master_doc, 'QBDI', 'QBDI Documentation',\n   author, 'QBDI', 'One line description of project.',\n   'Miscellaneous'),\n]\n\n# Documents to append as an appendix to all manuals.\n#texinfo_appendices = []\n\n# If false, no module index is generated.\n#texinfo_domain_indices = True\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n#texinfo_show_urls = 'footnote'\n\n# If true, do not generate a @detailmenu in the \"Top\" node's menu.\n#texinfo_no_detailmenu = False\n\n# -- Breathe --------------------------------------------------------------\n\nbreathe_projects = {\n    \"QBDI_CPP\": os.path.join(doxygen_dirs, \"doxygen_cpp\", \"xml\"),\n    \"QBDI_C\": os.path.join(doxygen_dirs, \"doxygen_c\", \"xml\"),\n    \"QBDIPRELOAD\": os.path.join(doxygen_dirs, \"doxygen_qbdipreload\", \"xml\"),\n}\n\nbreathe_use_project_refids = True\n\n# -- Sphinx-JS ------------------------------------------------------------\n\njs_source_path = os.path.join(current_dir, '..', '..', 'tools')\n"
  },
  {
    "path": "docs/source/dev.rst",
    "content": "Developer Documentation\n=======================\n\n.. toctree::\n    :maxdepth: 2\n\n    Compilation <compilation>\n    Repository Organization <repo_organization>\n    Testing <testing>\n    Technical Reference <techref>\n"
  },
  {
    "path": "docs/source/execblock.rst",
    "content": "ExecBlock\n=========\n\nIntroduction\n------------\n\nThe ExecBlock is a concept which tries to simplify the problem of context switching between the\nhost and the guest.\n\nThe main problem behind context switching is to be able to reference a memory location owned by the\nhost in the context of the guest. Loading the memory location in a register effectively destroys a\nguest value which would thus need to be saved somewhere. One could allow the usage of the guest\nstack and save values on it but this design has two major drawbacks. Firstly, although supposedly\nunused, this modifies guest memory and could have side effects if the program uses uninitialized\nstack values or in the case of unforeseen optimizations. Secondly, this assumes that the stack\nregisters always point to the stack or that a stack exists at all which might not be the case for\nmore exotic languages or assembly code. The only possible mechanism left is to use relative\naddressing load and store. While x86 and x86-64 allow 32-bit offset, ARM only allows 11-bit\noffset [#]_. The ExecBlock design thus only requires to be able to load and store general purpose\nregisters with an address offset up to 4096 bytes.\n\n.. note::\n\n    By default, AVX registers are also saved, one may want to disable this in cases where it is not\n    necessary, thus improving performance. It can be achieved using the environment flag\n    **QBDI_FORCE_DISABLE_AVX**.\n\n\nSome modern operating systems do not allow the allocation of memory pages with read, write and\nexecute permissions (RWX) for security reasons as this greatly facilitates remote code execution\nexploits. It is thus necessary to allocate two separate pages, with different permissions, for code\nand data. By exploiting the fact that most architectures use memory pages of 4096 bytes [#]_,\nallocating a data memory page next to a code memory page would allow the first instruction to\naddress at least the first address of the data memory page.\n\n.. [#] Thumb supports even less but this is not a problem as, with the exception of embedded ARM\n       architectures (the Cortex-M series), one can always switch between ARM mode and Thumb mode.\n.. [#] This is at least true for x86, x86_64, ARM, ARM64 and PowerPC.\n\nMemory Layout\n-------------\n\nAn ExecBlock is thus composed of two contiguous memory pages: the first one is called the code\nblock and has read and execute permissions (RX) and the second one is called the data block and has\nread and write permissions (RW).  The code block contains a prologue, responsible for the host to\nguest context switching, followed by the code of the translated basic block and finally the\nepilogue responsible for the guest to host switching. Both the prologue and the epilogue use the\nbeginning of the data block to store the context data. The context is split in three parts: the\nGPR context, the FPR context and the host context. The GPR and FPR context are straightforward\nand documented in the API itself as the GPRState and FPRState (see the State Management part of the\nAPI). The host context is used to store host data and a memory pointer called the selector. The\nselector is used by the prologue to determine on which basic block to jump next. The remaining\nspace in the data block is used for shadows which can be used to store any data needed by the\npatching or instrumentation process.\n\n.. image:: images/execblock_v3.svg\n\nReference\n---------\n\n.. doxygenclass:: QBDI::ExecBlock\n   :members:\n"
  },
  {
    "path": "docs/source/get_started-PyQBDIPreload.rst",
    "content": ".. currentmodule:: pyqbdi\n\n.. _get_started-pyqbdipreload:\n\nPyQBDIPreload\n=============\n\nPyQBDIPreload is an implementation of QBDIPreload for PyQBDI.\nIt allows users to inject the Python runtime into a target process and execute their own script in it.\nThe limitations are pretty much the same as those we face with QBDIPreload and PyQBDI:\n\n- For Linux and macOS the executable should be injectable with ``LD_PRELOAD`` or ``DYLD_INSERT_LIBRARIES``\n- PyQBDIPreload cannot be injected into a Python process\n- The Python runtime and the target must share the same architecture\n- An extra :class:`VM` must not be created. An already prepared :class:`VM` is provided to :func:`pyqbdipreload_on_run`.\n\n.. note::\n\n    For Linux and macOS the Python library ``libpython3.x.so`` must be installed.\n\nMain hook process\n-----------------\n\nUnlike QBDIPreload, the hook of the main function cannot be customised so you are unable to alter the hook process.\nThe Python interpreter is only initialised once the main function is hooked and the script is loaded.\nFurthermore, some modifications are made to the environment of the interpreter before the user script loading:\n\n- ``sys.argv`` are the arguments of the executable\n- ``LD_PRELOAD`` or ``DYLD_INSERT_LIBRARIES`` are removed from ``os.environ``\n- ``pyqbdi.__preload__`` is set to ``True``\n\nInstrumentation\n---------------\n\nOnce the script is loaded, the :func:`pyqbdipreload_on_run` function is called with a ready-to-run :class:`VM`.\nAny user callbacks should be registered to the :class:`VM`, then the VM can be run with :func:`pyqbdi.VM.run`.\n\n.. code:: python\n\n    import pyqbdi\n\n    def instCallback(vm, gpr, fpr, data):\n        # User code ...\n        return pyqbdi.CONTINUE\n\n    def pyqbdipreload_on_run(vm, start, stop):\n        vm.addCodeCB(pyqbdi.PREINST, instCallback, None)\n        vm.run(start, stop)\n\nExit hook\n---------\n\nThe ``atexit`` module is triggered when the execution is finished, or when ``exit`` or ``_exit`` are called.\n\n.. note::\n\n    Any :class:`VM` object is invalidated when the ``atexit`` module is triggered and should never be used.\n\nExecution\n---------\n\nA script called ``pyqbdipreload.py`` is provided to set up the environment and run the executable.\nThe first parameter is the PyQBDIPreload script. Next comes the binary, followed by its respective arguments if any.\n\n.. code:: bash\n\n    python3 -m pyqbdipreload <script> <executable> [<arguments> ...]\n\nFull example\n------------\n\nMerging everything we have learnt throughout this tutorial, we are now able to write our Python script\nwhich prints the instructions that are being executed by the target executable.\n\n.. include:: ../../examples/pyqbdi/trace_preload.py\n   :code:\n\nOn macOS\n--------\n\nApple silicon architecture\n++++++++++++++++++++++++++\n\nIn addition to the caveats discussed for QBDIPreload (see :ref:`macos-apple-silicon`), using PyQBDIPreload on ``arm64e`` binaries requires a Python interpreter built for the same ABI.\n\nExample commands for compiling OpenSSL and Python from sources for ``arm64e`` are provided in the workflow file ``.github/workflows/python_macos.yml``.\n"
  },
  {
    "path": "docs/source/get_started-QBDIPreload.rst",
    "content": ".. _get_started-qbdipreload:\n\nQBDIPreload\n===========\n\nQBDIPreload is a small utility library that provides code injection capabilities using dynamic library injection.\nIt works on Linux and macOS respectively with the ``LD_PRELOAD`` and ``DYLD_INSERT_LIBRARIES`` mechanisms, and on\nWindows using our ``QBDIWinPreloader.exe`` tool or a dll injection mechanism of your own choice.\n\nThanks to QBDIPreload, you can instrument the main function of an executable that has been dynamically linked.\nYou can also define various callbacks that are called at specific times throughout the execution.\n\nMain hook process\n-----------------\n\nTo use QBDIPreload, you must have a minimal codebase: a constructor and several *hook* functions.\nLike callbacks, hook functions are directly called by QBDIPreload.\n\nFirst of all, the constructor of QBDIPreload has to be initialised through declaring the macro :c:var:`QBDIPRELOAD_INIT`.\nIt's worth noting that this macro must be only be defined once in your code.\n\nThe :cpp:func:`qbdipreload_on_start` and :cpp:func:`qbdipreload_on_premain` hook functions are called at different stages during the execution of the programme.\nThey only need to return :c:var:`QBDIPRELOAD_NOT_HANDLED` if you don't want to modify the hook procedure.\n\n.. code:: c\n\n    #include \"QBDIPreload.h\"\n\n    QBDIPRELOAD_INIT;\n\n    int qbdipreload_on_start(void *main) {\n        return QBDIPRELOAD_NOT_HANDLED;\n    }\n\n    int qbdipreload_on_premain(void *gprCtx, void *fpuCtx) {\n        return QBDIPRELOAD_NOT_HANDLED;\n    }\n\nInstrumentation\n---------------\n\nOnce the main function is hooked by QBDIPreload, two methods are called: :cpp:func:`qbdipreload_on_main` and :cpp:func:`qbdipreload_on_run`.\n\nAt this point, you are able to capture the executable arguments inside of the :cpp:func:`qbdipreload_on_main` scope.\nThe :cpp:func:`qbdipreload_on_run` function is called right afterwards with a ready-to-run QBDI virtual machine as first argument.\nObviously, don't forget to register your callback(s) prior to running the VM.\n\n.. code:: c\n\n    static VMAction onInstruction(VMInstanceRef vm, GPRState *gprState, FPRState *fprState, void *data) {\n        // ...\n        return QBDI_CONTINUE;\n    }\n\n    int qbdipreload_on_main(int argc, char** argv) {\n        return QBDIPRELOAD_NOT_HANDLED;\n    }\n\n    int qbdipreload_on_run(VMInstanceRef vm, rword start, rword stop) {\n        // add user callbacks\n        qbdi_addCodeCB(vm, QBDI_PREINST, onInstruction, NULL);\n\n        // run the VM\n        qbdi_run(vm, start, stop);\n        return QBDIPRELOAD_NO_ERROR;\n    }\n\n.. note::\n   QBDIPreload automatically takes care of blacklisting instrumentation of the C standard library\n   and the OS loader as described in :ref:`intro_limitations`.\n\nExit hook\n---------\n\nQBDIPreload also intercepts calls to standard exit functions (``exit`` and ``_exit``).\nTypically, these are called when the executable is about to terminate.\nIf so, the :cpp:func:`qbdipreload_on_exit` method is called and can be used to save some data about the execution you want to keep before exiting.\nNote that the hook function is not called if the executable exits with a direct system call or a segmentation fault.\n\n.. code:: c\n\n    int qbdipreload_on_exit(int status) {\n        return QBDIPRELOAD_NO_ERROR;\n    }\n\nCompilation and execution\n-------------------------\n\nFinally, you need to compile your source code to a dynamic library.\nYour output binary has to be statically linked with both the QBDIPreload library and the QBDI library.\n\nThen, in order to test it against a target, simply running the following command should do the job:\n\n.. code:: bash\n\n    # on Linux\n    LD_BIND_NOW=1 LD_PRELOAD=./libqbdi_mytracer.so <executable> [<parameters> ...]\n\n    # on macOS\n    sudo DYLD_BIND_AT_LAUNCH=1 DYLD_INSERT_LIBRARIES=./libqbdi_mytracer.so <executable> [<parameters> ...]\n\n    # on Windows\n    QBDIWinPreloader.exe libqbdi_mytracer.dll <executable> [<parameters> ...]\n\n.. note::\n   QBDIWinPreloader.exe can be found in the installation bin folder of QBDI.\n   It's recommended to pass the full path of your injection DLL when using this tool.\n\nAs the loader is not in the instrumentation range, we recommend setting ``LD_BIND_NOW`` or ``DYLD_BIND_AT_LAUNCH``\nin order to resolve and bind all symbols before the instrumentation.\n\nFull example\n------------\n\nMerging everything we have learnt throughout this tutorial, we are now able to write our C/C++ source code files.\nIn the following examples, we aim at displaying every executed instruction of the binary we are running against.\n\n.. _get_started-preload-c:\n\nQBDIPreload in C\n++++++++++++++++\n\n.. include:: ../../examples/c/tracer_preload.c\n   :code:\n\n.. _get_started-preload-cpp:\n\nQBDIPreload in C++\n++++++++++++++++++\n\n.. include:: ../../examples/cpp/tracer_preload.cpp\n   :code:\n\n.. _qbdi_preload_template:\n\nGenerate a template\n-------------------\n\nA QBDI template can be considered as a baseline project, a minimal component you can modify and build your instrumentation tool on.\nThey are provided to help you effortlessly start off a new QBDI-based project.\nThe binary responsible for generating a template is shipped in the release packages and can be used as follows:\n\n.. code:: bash\n\n    mkdir QBDIPreload && cd QBDIPreload\n    qbdi-preload-template\n    mkdir build && cd build\n    cmake ..\n    make\n\n\nOn macOS\n--------\n\n.. _macos-apple-silicon:\n\nApple silicon architecture\n++++++++++++++++++++++++++\n\n(This information was last verified on macOS Tahoe 26.0.1. Other versions might differ slightly.)\n\nOn Apple silicon, binaries can be compiled for two different ABIs, i.e. ``arm64`` and ``arm64e``.\nQBDI should be compiled with the matching ABIs, by adding ``-DCMAKE_OSX_ARCHITECTURES=\"arm64\"`` or ``-DCMAKE_OSX_ARCHITECTURES=\"arm64e\"`` in CMake command line.\nTo check which ABI a binary was compiled for, use:\n\n.. code:: bash\n\n    lipo -archs $BINARY_PATH\n\nFor user-built binaries, QBDIPreload should function correctly without any additional setup, provided that a QBDIPreload version matching the target ABI is used.\n\nInjecting into system binaries, which are always ``arm64e``, requires, at minimum, disabling `System Integrity Protection <https://support.apple.com/en-us/HT204899>`_.\nFor example, doing so makes it possible to inject QBDIPreload into ``/bin/ls``.\n\nOther platform binaries, such as ``imagent``, additionally require disabling Apple Mobile File Integrity (AMFI),\ni.e. by setting the appropriate boot-arg with:\n\n.. code:: bash\n\n    sudo nvram boot-args='amfi_get_out_of_my_way=1'\n\nDisabling AMFI may break some apps.\n\nTo troubleshoot injection failures, review system log messages using the Console application.\n\nIntel architecture\n++++++++++++++++++\n\n(This information hasn't been recently verified and may be outdated.)\n\nFor user-built binaries, QBDIPreload should function correctly without any additional setup.\n\nFor system binaries, the same caveats described for the Apple silicon architecture likely apply.\n"
  },
  {
    "path": "docs/source/get_started-c.rst",
    "content": "C API\n=====\n\nA step-by-step example illustrating a basic (yet powerful) usage of the QBDI C API.\n\nLoad the target code\n--------------------\n\nIn this tutorial, we aim at figuring out how many iterations a Fibonacci function is doing.\nTo do so, we will rely on QBDI to instrument the function.\nFor convenience's sake, its source code is compiled along with the one we are about to write.\n\n.. code:: c\n\n    int fibonacci(int n) {\n        if(n <= 2)\n            return 1;\n        return fibonacci(n-1) + fibonacci(n-2);\n    }\n\nHowever, it's not always the case.\nSometimes, we need to look into a function we don't have the source code of -- that is, it has already been compiled.\nAs a result, we have to find a way to load the code we want to inspect into our process' memory space.\nFor instance, if the function of interest is embedded in a dynamic library, we can link our code with this library when compiling\nor import it at runtime by calling either ``dlopen`` or ``LoadLibraryA``.\n\nNote that if you want to instrument a whole binary, QBDIPreload should be preferred (see :ref:`get_started-preload-c`).\n\nInitialise the virtual machine\n------------------------------\n\nFirst off, we need to initialise the virtual machine (often referred to as VM) itself. The type ``VMInstanceRef`` represents an instance of the VM.\nThe :cpp:func:`qbdi_initVM` function needs to be called to set the instance up.\nThe second, third and fourth arguments are used to customise the instance depending on what you want to do.\n\n.. code:: c\n\n    #include \"QBDI.h\"\n\n    VMInstanceRef vm;\n    qbdi_initVM(&vm, NULL, NULL, 0);\n\nRetrieve the VM context\n-----------------------\n\nPrior to initialising the virtual stack (see next section),\nwe need to get a pointer to the virtual machine state (:cpp:type:`GPRState`), which can be obtained by calling :cpp:func:`qbdi_getGPRState`.\nThis object represents the current VM's context.\n\n.. code:: c\n\n    GPRState *state = qbdi_getGPRState(vm);\n    assert(state != NULL);\n\nAllocate a virtual stack\n------------------------\n\nThe virtual machine does not work with the regular stack that your process uses -- instead, QBDI needs its own stack.\nTherefore, we have to ask for a virtual stack using :cpp:func:`qbdi_allocateVirtualStack`. This function is\nresponsible for allocating an aligned memory space, setting the stack pointer register accordingly and returning the top address of this brand-new memory region.\n\n.. code:: c\n\n    uint8_t* fakestack;\n    bool res = qbdi_allocateVirtualStack(state, STACK_SIZE, &fakestack);\n    assert(res == true);\n\n\nWrite our first callback function\n---------------------------------\n\nNow that the virtual machine has been set up, we can start playing with QBDI core features.\n\nTo have a trace of our execution, we will need a callback that will retrieve the current address\nand the disassembly of the instruction and print it.\n\nAs the callback will be called on an instruction, the callback must follow the :cpp:type:`InstCallback` type. Inside the\ncallback, we can get an :cpp:struct:`InstAnalysis` of the current instruction with :cpp:func:`qbdi_getInstAnalysis`.\nTo have the address and the disassembly, the :cpp:struct:`InstAnalysis` needs to have the type\n:cpp:enumerator:`QBDI_ANALYSIS_INSTRUCTION <AnalysisType::QBDI_ANALYSIS_INSTRUCTION>` (for the address) and\n:cpp:enumerator:`QBDI_ANALYSIS_DISASSEMBLY <AnalysisType::QBDI_ANALYSIS_DISASSEMBLY>` (for the disassembly).\n\n\n.. code:: c\n\n    VMAction showInstruction(VMInstanceRef vm, GPRState *gprState, FPRState *fprState, void *data) {\n        // Obtain an analysis of the instruction from the VM\n        const InstAnalysis* instAnalysis = qbdi_getInstAnalysis(vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);\n\n        // Printing disassembly\n        printf(\"0x%\" PRIRWORD \": %s\\n\", instAnalysis->address, instAnalysis->disassembly);\n\n        return QBDI_CONTINUE;\n    }\n\nAn :cpp:type:`InstCallback` must always return an action (:cpp:enum:`VMAction`) to the VM to specify if the execution should\ncontinue or stop. In most cases :cpp:enumerator:`QBDI_CONTINUE <VMAction::QBDI_CONTINUE>`\nshould be returned to continue the execution.\n\nRegister a callback\n-------------------\n\nThe callback must be registered in the VM. The function :cpp:func:`qbdi_addCodeCB` allows registering\na callback for every instruction. The callback can be called before the instruction\n(:cpp:enumerator:`QBDI_PREINST <InstPosition::QBDI_PREINST>`) or after the instruction\n(:cpp:enumerator:`QBDI_POSTINST <InstPosition::QBDI_POSTINST>`).\n\n.. code:: c\n\n    uint32_t cid = qbdi_addCodeCB(vm, QBDI_PREINST, showInstruction, NULL, 0);\n    assert(cid != QBDI_INVALID_EVENTID);\n\nThe function returns a callback ID or the special ID :cpp:enumerator:`QBDI_INVALID_EVENTID <VMError::QBDI_INVALID_EVENTID>` if\nthe registration fails. The callback ID can be kept if you want to unregister the callback later.\n\nCount the iterations\n--------------------\n\nWith the current implementation of Fibonacci, the function will iterate by recursively calling itself.\nConsequently, we can determine the number of iterations the function is doing by counting the number of calls.\n:cpp:func:`qbdi_addMnemonicCB` can be used to register a callback which is solely called when encountering specific instructions.\nAll QBDI callbacks allow users to pass a custom parameter ``data`` of type ``void *``.\n\n.. code:: c\n\n    VMAction countIteration(VMInstanceRef vm, GPRState *gprState, FPRState *fprState, void *data) {\n        (*((unsigned*) data))++;\n\n        return QBDI_CONTINUE;\n    }\n\n    unsigned iterationCount = 0;\n    qbdi_addMnemonicCB(vm, \"CALL*\", QBDI_PREINST, countIteration, &iterationCount, 0);\n\n\nSet instrumented ranges\n-----------------------\n\nQBDI needs a range of addresses where the code should be instrumented. If the execution goes out of this scope,\nQBDI will try to restore an uninstrumented execution.\n\nIn our example, we need to include the function we are looking into in the instrumented range. :cpp:func:`qbdi_addInstrumentedModuleFromAddr`\ncan be used to add a whole module (binary or library) in the range of instrumentation with a single address of this module.\n\n.. code:: c\n\n    res = qbdi_addInstrumentedModuleFromAddr(vm, (rword) &fibonacci);\n    assert(res == true);\n\nRun the instrumentation\n-----------------------\n\nWe can finally run the instrumentation using the :cpp:func:`qbdi_call` function.\nIt aligns the stack, sets the argument(s) (if needed) and a fake return address and\ncalls the target function through QBDI. The execution stops when the instrumented code returns to the\nfake address.\n\n.. code:: c\n\n    rword retval;\n    res = qbdi_call(vm, &retval, (rword) fibonacci, 1, 25);\n    assert(res == true);\n\n:cpp:func:`qbdi_call` returns when the function has completely run in the context of QBDI.\nThe first argument has been filled with the value of the return register (e.g. ``RAX`` for X86_64).\n\nIt may turn out that the function does not expect the calling convention :cpp:func:`qbdi_call` uses.\nIn this precise case, you must set up the proper context and the stack yourself and call :cpp:func:`qbdi_run` afterwards.\n\nTerminate the execution properly\n--------------------------------\n\nAt last, before exiting, we need to free up the resources we have allocated: both the virtual stack and the virtual machine by respectively calling :cpp:func:`qbdi_alignedFree` and :cpp:func:`qbdi_terminateVM`.\n\n.. code:: c\n\n    qbdi_alignedFree(fakestack);\n    qbdi_terminateVM(vm);\n\nFull example\n------------\n\nMerging everything we have learnt throughout this tutorial, we are now able to write our C source code file:\n\n.. include:: ../../examples/c/fibonacci.c\n   :code:\n\nGenerate a template\n-------------------\n\nA QBDI template can be considered as a baseline project, a minimal component you can modify and build your instrumentation tool on.\nThey are provided to help you effortlessly start off a new QBDI-based project.\nThe binary responsible for generating a template is shipped in the release packages and can be used as follows:\n\n.. code:: bash\n\n    mkdir new_project\n    cd new_project\n    qbdi-template\n\nA template consists of a simple C source code file and a basic CMake build script.\nA file called ``README.txt`` is also present, it describes the compilation procedure.\n"
  },
  {
    "path": "docs/source/get_started-cpp.rst",
    "content": "C++ API\n=======\n\nA step-by-step example illustrating a basic (yet powerful) usage of the QBDI C++ API.\n\nLoad the target code\n--------------------\n\nIn this tutorial, we aim at figuring out how many iterations a Fibonacci function is doing.\nTo do so, we will rely on QBDI to instrument the function.\nFor convenience's sake, its source code is compiled along with the one we are about to write.\n\n.. code:: c\n\n    int fibonacci(int n) {\n        if(n <= 2)\n            return 1;\n        return fibonacci(n-1) + fibonacci(n-2);\n    }\n\nHowever, it's not always the case.\nSometimes, we need to look into a function we don't have the source code of -- that is, it has already been compiled.\nAs a result, we have to find a way to load the code we want to inspect into our process' memory space.\nFor instance, if the function of interest is embedded in a dynamic library, we can link our code with this library when compiling\nor import it at runtime by calling either ``dlopen`` or ``LoadLibraryA``.\n\nNote that if you want to instrument a whole binary, QBDIPreload should be preferred (see :ref:`get_started-preload-cpp`).\n\nInitialise the virtual machine\n------------------------------\n\nFirst off, we need to initialise the virtual machine (:cpp:class:`QBDI::VM`) itself.\n\n.. code:: cpp\n\n    #include \"QBDI.h\"\n\n    QBDI::VM vm;\n\nRetrieve the VM context\n-----------------------\n\nPrior to initialising the virtual stack (see next section),\nwe need to get a pointer to the virtual machine state (:cpp:type:`QBDI::GPRState`), which can be obtained by calling :cpp:func:`QBDI::VM::getGPRState`.\nThis object represents the current VM's context.\n\n.. code:: cpp\n\n    QBDI::GPRState *state = vm.getGPRState();\n    assert(state != NULL);\n\nAllocate a virtual stack\n------------------------\n\nThe virtual machine does not work with the regular stack that your process uses -- instead, QBDI needs its own stack.\nTherefore, we have to ask for a virtual stack using :cpp:func:`QBDI::allocateVirtualStack`. This function is\nresponsible for allocating an aligned memory space, setting the stack pointer register accordingly and returning the top address of this brand-new memory region.\n\n.. code:: cpp\n\n    uint8_t* fakestack;\n    bool res = QBDI::allocateVirtualStack(state, STACK_SIZE, &fakestack);\n    assert(res == true);\n\nOur first callback function\n---------------------------\n\nNow that the virtual machine has been set up, we can start playing with QBDI core features.\n\nTo have a trace of our execution, we will need a callback that will retrieve the current address\nand the disassembly of the instruction and print it.\n\nAs the callback will be called on an instruction, the callback must follow the :cpp:type:`QBDI::InstCallback` type. Inside the\ncallback, we can get an :cpp:struct:`QBDI::InstAnalysis` of the current instruction with :cpp:func:`QBDI::VM::getInstAnalysis`.\nTo have the address and the disassembly, the :cpp:struct:`QBDI::InstAnalysis` needs to have the type\n:cpp:enumerator:`QBDI::AnalysisType::ANALYSIS_INSTRUCTION` (for the address) and\n:cpp:enumerator:`QBDI::AnalysisType::ANALYSIS_DISASSEMBLY` (for the disassembly). These\ntwo :cpp:enum:`QBDI::AnalysisType` are the default parameters of :cpp:func:`QBDI::VM::getInstAnalysis` and\ncan be omitted.\n\n\n.. code:: cpp\n\n    QBDI::VMAction showInstruction(QBDI::VM* vm, QBDI::GPRState *gprState, QBDI::FPRState *fprState, void *data) {\n        // Obtain an analysis of the instruction from the VM\n        const QBDI::InstAnalysis* instAnalysis = vm->getInstAnalysis();\n\n        // Printing disassembly\n        std::cout << std::setbase(16) << instAnalysis->address << \": \"\n                  << instAnalysis->disassembly << std::endl << std::setbase(10);\n\n        return QBDI::VMAction::CONTINUE;\n    }\n\nAn :cpp:type:`QBDI::InstCallback` must always return an action (:cpp:enum:`QBDI::VMAction`) to the VM to specify if the execution should\ncontinue or stop. In most cases :cpp:enumerator:`QBDI::VMAction::CONTINUE`\nshould be returned to continue the execution.\n\nRegister a callback\n-------------------\n\nThe callback must be registered in the VM. The function :cpp:func:`QBDI::VM::addCodeCB` allows registering\na callback for every instruction. The callback can be called before the instruction\n(:cpp:enumerator:`QBDI::InstPosition::PREINST`) or after the instruction\n(:cpp:enumerator:`QBDI::InstPosition::POSTINST`).\n\n.. code:: cpp\n\n    uint32_t cid = vm.addCodeCB(QBDI::PREINST, showInstruction, nullptr);\n    assert(cid != QBDI::INVALID_EVENTID);\n\nThe function returns a callback ID or the special ID :cpp:enumerator:`QBDI::VMError::INVALID_EVENTID` if\nthe registration fails. The callback ID can be kept if you want to unregister the callback later.\n\nCount the iterations\n--------------------\n\nWith the current implementation of Fibonacci, the function will iterate by recursively calling itself.\nConsequently, we can determine the number of iterations the function is doing by counting the number of calls.\n:cpp:func:`QBDI::VM::addMnemonicCB` can be used to register a callback which is solely called when encountering specific instructions.\nAll QBDI callbacks allow users to pass a custom parameter ``data`` of type ``void *``.\n\n.. code:: cpp\n\n    QBDI::VMAction countIteration(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState, QBDI::FPRState *fprState, void *data) {\n        (*((unsigned*) data))++;\n\n        return QBDI::CONTINUE;\n    }\n\n    unsigned iterationCount = 0;\n    vm.addMnemonicCB(\"CALL*\", QBDI::PREINST, countIteration, &iterationCount);\n\n\nSet instrumented ranges\n-----------------------\n\nQBDI needs a range of addresses where the code should be instrumented. If the execution goes out of this scope,\nQBDI will try to restore an uninstrumented execution.\n\nIn our example, we need to include the method in the instrumented range. The method :cpp:func:`QBDI::VM::addInstrumentedModuleFromAddr`\ncan be used to add a whole module (binary or library) in the range of instrumentation with a single address of this module.\n\n.. code:: cpp\n\n    res = vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(fibonacci));\n    assert(res == true);\n\nRun the instrumentation\n-----------------------\n\nWe can finally run the instrumentation using the :cpp:func:`QBDI::VM::call` function.\nIt aligns the stack, sets the argument(s) (if needed) and a fake return address and\ncalls the target function through QBDI. The execution stops when the instrumented code returns to the\nfake address.\n\n.. code:: cpp\n\n    QBDI::rword retval;\n    res = vm.call(&retval, reinterpret_cast<QBDI::rword>(fibonacci), {25});\n    assert(res == true);\n\n:cpp:func:`QBDI::VM::call` returns when the function has completely run in the context of QBDI.\nThe first argument has been filled with the value of the return register (e.g. ``RAX`` for X86_64).\n\nIt may turn out that the function does not expect the calling convention :cpp:func:`QBDI::VM::call` uses.\nIn this precise case, you must set up the proper context and the stack yourself and call :cpp:func:`QBDI::VM::run` afterwards.\n\nTerminate the execution properly\n--------------------------------\n\nAt last, before exiting, we need to free up the virtual stack we have allocated by calling :cpp:func:`QBDI::alignedFree`.\n\n.. code:: cpp\n\n    QBDI::alignedFree(fakestack);\n\nFull example\n------------\n\nMerging everything we have learnt throughout this tutorial, we are now able to write our C++ source code file:\n\n.. include:: ../../examples/cpp/fibonacci.cpp\n   :code:\n"
  },
  {
    "path": "docs/source/get_started-frida.rst",
    "content": ".. highlight:: javascript\n\nFrida/QBDI\n==========\n\nQBDI can team up with Frida to be even more powerful together.\nTo be able to use QBDI bindings while injected into a process with Frida,\nit is necessary to understand how to use Frida to perform some common tasks beforehand.\nThrough this simple example based on *qbdi-frida-template* (see :ref:`qbdi-frida-template`), we will explain a basic usage of Frida and QBDI.\n\nCommon tasks\n------------\n\nThis section solely shows a few basic actions and gives you a general overview of what you can do with Frida.\nKeep in mind that Frida offers many more awesome features, all listed in the `JavaScript API documentation <https://frida.re/docs/javascript-api/>`_.\n\nRead memory\n+++++++++++\n\nSometimes it may be necessary to have a look at a buffer or specific part of the memory. We rely on Frida to do it.\n\n.. code-block:: javascript\n\n    var arrayPtr = ptr(0xDEADBEEF)\n    var size = 0x80\n    var buffer = Memory.readByteArray(arrayPtr, size)\n\n\nWrite memory\n++++++++++++\n\nWe also need to be able to write memory:\n\n.. code-block:: javascript\n\n    var arrayPtr = ptr(0xDEADBEEF)\n    var size = 0x80\n    var toWrite = new Uint8Array(size);\n    // fill your buffer eventually\n    Memory.writeByteArray(arrayPtr, toWrite)\n\n\nAllocate an array\n+++++++++++++++++\n\nIf you have a function that takes a buffer or a string as an input, you might need to allocate a new buffer using Frida:\n\n.. code-block:: javascript\n\n    // allocate and write a 2-byte buffer\n    var buffer = Memory.alloc(2);\n    Memory.writeByteArray(buffer, [0x42, 0x42])\n    // allocate and write an UTF8 string\n    var str = Memory.allocUtf8String(\"Hello World !\");\n\n\nInitialise a VM object\n++++++++++++++++++++++\n\nIf `frida-qbdi.js` (or a script requiring it) is successfully loaded in Frida,\na new :js:class:`VM` object becomes available.\nIt provides an object oriented access to the framework features.\n\n.. code-block:: javascript\n\n    // initialise QBDI\n    var vm = new VM();\n    console.log(\"QBDI version is \" + vm.version.string);\n    var state = vm.getGPRState();\n\n\nInstrument a function with QBDI\n+++++++++++++++++++++++++++++++\n\nYou can instrument a function using QBDI bindings. They are really close to the C++ ones,\nfurther details about the exposed APIs are available in the :ref:`frida-qbdi-api` documentation.\n\n.. code-block:: javascript\n\n    var functionPtr = DebugSymbol.fromName(\"function_name\").address;\n    vm.addInstrumentedModule(\"demo.bin\");\n\n    var InstructionCallback = vm.newInstCallback(function(vm, gpr, fpr, data) {\n        inst = vm.getInstAnalysis();\n        gpr.dump(); // display the context\n        console.log(\"0x\" + inst.address.toString(16) + \" \" + inst.disassembly); // display the instruction\n        return VMAction.CONTINUE;\n    });\n    var iid = vm.addCodeCB(InstPosition.PREINST, instructionCallback, NULL);\n\n    vm.call(functionPtr, []);\n\n\nIf you ever want to pass custom arguments to your callback, this can be done via the **data** argument:\n\n.. code-block:: javascript\n\n    // this callback is used to count the number of basic blocks executed\n    var userData = { counter: 0};\n    var BasicBlockCallback = vm.newVMCallback(function(vm, evt, gpr, fpr, data) {\n        data.counter++;\n        return VMAction.CONTINUE;\n    });\n    vm.addVMEventCB(VMEvent.BASIC_BLOCK_ENTRY, BasicBlockCallback, userData);\n    console.log(userData.counter);\n\n\nScripts\n+++++++\n\nBindings can simply be used in Frida REPL, or imported in a Frida script, empowering\nthe bindings with all the `nodejs` ecosystem.\n\n.. code-block:: javascript\n\n    import { VM } from \"./frida-qbdi.js\";\n\n    var vm = new VM();\n    console.log(\"QBDI version is \" + vm.version.string);\n\nIt will be possible to load it in Frida in place of `frida-qbdi.js`, allowing to\neasily create custom instrumentation tools with in-process scripts written in\nJavaScript and external control in Python (or any language supported by `Frida`).\n\nCompilation\n+++++++++++\n\nIn order to actually import QBDI bindings into your project, your script needs to be *compiled* with the `frida-compile <https://www.npmjs.com/package/frida-compile>`_ utility.\nInstalling it requires you to have ``npm`` installed. The `babelify package <https://www.npmjs.com/package/babelify>`_ might be also needed.\nOtherwise, you will not be able to successfully compile/load it and some errors will show up once running it with Frida.\n\nBefore running frida-compile, be sure that the script ``frida-qbdi.js`` is\ninside your current directory.\n\n.. code:: bash\n\n    find <archive_extracted_path> -name frida-qbdi.js -exec cp {} . \\;\n\n    # if frida-compile is not already installed\n    npm install frida-compile babelify\n    ./node_modules/.bin/frida-compile MyScript.js -o MyScriptCompiled.js\n    # else\n    frida-compile MyScript.js -o MyScriptCompiled.js\n\nRun Frida/QBDI on a workstation\n+++++++++++++++++++++++++++++++\n\nTo use QBDI on an already existing process you can use the following syntax:\n\n.. code:: bash\n\n    frida -n processName -l MyScriptCompiled.js\n\nYou can also spawn the process using Frida to instrument it with QBDI as soon as it starts:\n\n.. code:: bash\n\n    frida -f binaryPath Arguments -l MyScriptCompiled.js\n\nRun Frida/QBDI on an Android device\n+++++++++++++++++++++++++++++++++++\n\nSince Frida provides a great interface to instrument various types of target, we can also rely on it to use QBDI on Android, especially when it comes to inspecting applications.\nNevertheless, it has some specificities you need to be aware of.\nBefore running your script, make sure that:\n\n- a Frida server is running on the remote device and is reachable from your workstation\n- the ``libQBDI.so`` library has been placed in ``/data/local/tmp`` (available in `Android packages <https://github.com/QBDI/QBDI/releases/>`_)\n- SELinux has been turned into `permissive` mode (through ``setenforce 0``)\n\nThen, you should be able to inject your script into a specific Android application:\n\n.. code:: bash\n\n    # if the application is already running\n    frida -Un com.app.example -l MyScriptCompiled.js\n\n    # if you want to spawn the application\n    frida -Uf com.app.example -l MyScriptCompiled.js\n\nConcrete example\n----------------\n\nIf you have already had a look at the default instrumentation of the template generated with *qbdi-frida-template* (see :ref:`qbdi-frida-template`), you are probably familiar with the following example.\nRoughly speaking, what it does is create a native call to the *Secret()* function, and instrument it looking for *XOR*.\n\nSource code\n+++++++++++\n\n.. literalinclude:: ../../templates/qbdi_frida_template/SimpleXOR.c\n    :language: C\n\n\nFrida/QBDI script\n+++++++++++++++++\n\n.. literalinclude:: ../../templates/qbdi_frida_template/FridaQBDI_sample.js\n    :language: javascript\n\n.. _qbdi-frida-template:\n\nGenerate a template\n-------------------\n\nA QBDI template can be considered as a baseline project, a minimal component you can modify and build your instrumentation tool on.\nThey are provided to help you effortlessly start off a new QBDI-based project.\nIf you want to get started using QBDI bindings, you can create a brand-new default project doing:\n\n.. code:: bash\n\n    make NewProject\n    cd NewProject\n    qbdi-frida-template\n\n    # if you want to build the demo binary\n    mkdir build && cd build\n    cmake ..\n    make\n\n    # if frida-compile is not already installed\n    npm install frida-compile babelify\n    ./node_modules/.bin/frida-compile ../FridaQBDI_sample.js -o RunMe.js\n    # else\n    frida-compile ../FridaQBDI_sample.js -o RunMe.js\n\n    frida -f ./demo.bin -l ./RunMe.js\n"
  },
  {
    "path": "docs/source/get_started-pyqbdi.rst",
    "content": ".. currentmodule:: pyqbdi\n\n.. _get_started-pyqbdi:\n\nPyQBDI\n======\n\nPyQBDI brings Python3 bindings over the QBDI API.\nThat way, you can take advantage of the QBDI features directly from your Python scripts without bothering using C/C++.\nIt may be pretty useful if you need to build something quickly.\nHowever, it introduces some limitations:\n\n- PyQBDI cannot be used to instrument a Python process\n- The performances are poorer than when using the C/C++ APIs\n- The Python runtime's and the target's architectures must be the same\n\nMemory allocation\n-----------------\n\nUnlike the C/C++ APIs, interacting with the process' memory is much more complicated while in Python -- that is, memory regions cannot be allocated, read or written.\nLuckily, PyQBDI offers helpers to allow users to perform these actions.\n\n.. code:: python\n\n    import pyqbdi\n\n    value = b\"bytes array\"\n\n    addr = pyqbdi.allocateMemory(len(value))\n    pyqbdi.writeMemory(addr, value)\n    value2 = pyqbdi.readMemory(addr, len(value))\n    assert value == value2\n    pyqbdi.freeMemory(addr)\n\nLoad the target code\n--------------------\n\nIn this tutorial, we aim at executing the ``foo`` function which lies in a shared library whose name is ``mylib.so``, in the context of QBDI.\nPyQBDI will give us a hand doing so.\n\n.. code:: python\n\n    import pyqbdi\n    import ctypes\n\n    mylib = ctypes.cdll.LoadLibrary(\"mylib.so\")\n    funcPtr = ctypes.cast(mylib.foo, ctypes.c_void_p).value\n\nNote that if you want to instrument a whole binary, PyQBDIPreload should be preferred (see :ref:`get_started-pyqbdipreload`).\n\nInitialise the virtual machine\n------------------------------\n\nFirst off, we need to initialise the virtual machine (:class:`VM`) itself.\nCalling the :func:`pyqbdi.VM` is needed to craft a new instance.\n\n.. code:: python\n\n    vm = pyqbdi.VM()\n\nAllocate a virtual stack\n------------------------\n\nThe virtual machine does not work with the regular stack that your process uses -- instead, QBDI needs its own stack.\nTherefore, we have to ask for a virtual stack using :func:`pyqbdi.allocateVirtualStack`. This function is\nresponsible for allocating an aligned memory space, setting the stack pointer register accordingly and returning the top address of this brand-new memory region.\n\n.. code:: python\n\n    state = vm.getGPRState()\n    fakestack = pyqbdi.allocateVirtualStack(state, 0x100000)\n    assert fakestack != None\n\n\nWrite our first callback function\n---------------------------------\n\nNow that the virtual machine has been set up, we can start playing with QBDI core features.\n\nTo have a trace of our execution, we will need a callback that will retrieve the current address\nand the disassembly of the instruction and print it.\n\nAs the callback will be called on an instruction, the callback must follow the :func:`InstCallback <pyqbdi.InstCallback>` type. Inside the\ncallback, we can get an :class:`InstAnalysis` of the current instruction with :func:`pyqbdi.VM.getInstAnalysis`.\nTo have the address and the disassembly, the :class:`InstAnalysis` needs to have the type\n:data:`pyqbdi.ANALYSIS_INSTRUCTION <pyqbdi.AnalysisType>` (for the address) and\n:data:`pyqbdi.ANALYSIS_DISASSEMBLY <pyqbdi.AnalysisType>` (for the disassembly). These\ntwo :data:`pyqbdi.AnalysisType` are the default parameters of :func:`pyqbdi.VM.getInstAnalysis` and\ncan be omitted.\n\n.. code:: python\n\n    def showInstruction(vm, gpr, fpr, data):\n        # Obtain an analysis of the instruction from the VM\n        instAnalysis = vm.getInstAnalysis()\n\n        # Printing disassembly\n        print(\"0x{:x}: {}\".format(instAnalysis.address, instAnalysis.disassembly))\n\n        return pyqbdi.CONTINUE\n\nAn :func:`InstCallback <pyqbdi.InstCallback>` must always return an action (:data:`pyqbdi.VMAction`) to the VM to specify if the execution should\ncontinue or stop. In most cases :data:`CONTINUE <pyqbdi.VMAction>`\nshould be returned to continue the execution.\n\nRegister a callback\n-------------------\n\nThe callback must be registered in the VM. The function :func:`pyqbdi.VM.addCodeCB` allows registering\na callback for every instruction. The callback can be called before the instruction\n(:data:`pyqbdi.PREINST <pyqbdi.InstPosition>`) or after the instruction\n(:data:`pyqbdi.POSTINST <pyqbdi.InstPosition>`).\n\n.. code:: python\n\n    cid = vm.addCodeCB(pyqbdi.PREINST, showInstruction, None)\n    assert cid != pyqbdi.INVALID_EVENTID\n\nThe function returns a callback ID or the special ID :data:`pyqbdi.INVALID_EVENTID <pyqbdi.VMError>` if\nthe registration fails. The callback ID can be kept if you want to unregister the callback later.\n\nSet instrumented ranges\n-----------------------\n\nQBDI needs a range of addresses where the code should be instrumented. If the execution goes out of this scope,\nQBDI will try to restore an uninstrumented execution.\n\nIn our example, we need to include the method in the instrumented range. The method :func:`pyqbdi.VM.addInstrumentedModuleFromAddr`\ncan be used to add a whole module (binary or library) in the instrumentation range with a single address of this module.\n\n.. code:: python\n\n    assert vm.addInstrumentedModuleFromAddr(funcPtr)\n\nRun the instrumentation\n-----------------------\n\nWe can finally run the instrumentation using the :func:`pyqbdi.VM.call` function.\nIt aligns the stack, sets the argument(s) (if needed) and a fake return address and\ncalls the target function through QBDI. The execution stops when the instrumented code returns to the\nfake address.\n\n.. code:: python\n\n    asrun, retval = vm.call(funcPtr, [args1, args2])\n    assert asrun\n\n:func:`pyqbdi.VM.call` returns when the function has completely run in the context of QBDI.\nThe first return value has been filled with the value of the return register (e.g. ``RAX`` for X86_64).\n\nIt may turn out that the function does not expect the calling convention :func:`pyqbdi.VM.call` uses.\nIn this precise case, you must set up the proper context and the stack yourself and call :func:`pyqbdi.VM.run` afterwards.\n\nTerminate the execution properly\n--------------------------------\n\nAt last, before exiting, we need to free up the virtual stack we have allocated by calling :func:`pyqbdi.alignedFree`.\n\n.. code:: python\n\n    pyqbdi.alignedFree(fakestack)\n\nFull example\n------------\n\nMerging everything we have learnt throughout this tutorial, we are now able to solve real problems.\nFor instance, the following example shows how one can generate an execution trace of the ``sin`` function by using a PyQBDI script:\n\n.. include:: ../../examples/pyqbdi/trace_sin.py\n   :code:\n\n"
  },
  {
    "path": "docs/source/get_started.rst",
    "content": "Getting Started\n===============\n\nThe following sections explain how one can take advantage of QBDI through different use cases.\n\n.. toctree::\n    :maxdepth: 2\n\n    C API <get_started-c>\n    C++ API <get_started-cpp>\n    PyQBDI <get_started-pyqbdi>\n    Frida/QBDI <get_started-frida>\n    QBDIPreload <get_started-QBDIPreload>\n    PyQBDIPreload <get_started-PyQBDIPreload>\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": ".. QBDI documentation master file, created by\n   sphinx-quickstart on Mon Nov  2 10:36:40 2015.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nQBDI Documentation\n==================\n\n.. include:: ../../README.rst\n    :start-after: .. intro\n    :end-before: .. intro-end\n\nContents\n++++++++\n\n.. toctree::\n   :maxdepth: 2\n\n    Introduction <intro>\n    Installation and Integration <installation_and_integration>\n    Getting Started <get_started>\n    Tutorial <tutorial>\n    API Reference <api>\n    Architecture Support <architecture_support>\n    Developer Documentation <dev>\n    Changelog <changelog>\n    References <references>\n\nIndices and Tables\n==================\n\n* :ref:`genindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/source/installation_and_integration.rst",
    "content": "Installation and Integration\n============================\n\n\nC/C++ API installation\n----------------------\n\nWhen a new stable version of QBDI is released, prebuilt packages can be downloaded through the `release page <https://github.com/QBDI/QBDI/releases>`_ on GitHub. In order to make sure you have downloaded the right package, you can verify its integrity with the file ``SHA256``.\n\nA GPG signature of ``SHA256`` by our `developer key (2763 2215 DED8 D717 AD08 477D 874D 3F16 4D45 2193) <https://qbdi.quarkslab.com/qbdi.asc>`_ is available in ``SHA256.sig``.\n\n.. code-block:: bash\n\n    # check the hash of the prebuilt package\n    sha256 -c SHA256\n\n    # verify the signature\n    wget https://qbdi.quarkslab.com/qbdi.asc -O - | gpg --import -\n    gpg --verify SHA256.sig\n\n\nDebian / Ubuntu\n+++++++++++++++\n\nDebian and Ubuntu packages are provided for stable and LTS releases, and can be installed using ``dpkg``:\n\n.. code-block:: bash\n\n    dpkg -i QBDI-*-*-X86_64.deb\n\n\nArch Linux\n++++++++++\n\nArch Linux packages can be installed using ``pacman``:\n\n.. code-block:: bash\n\n    pacman -U QBDI-*-*-X86_64.tar.zst\n\n\nmacOS\n+++++\n\nA software installer is provided for macOS. Opening the ``.pkg`` in Finder and following the\ninstructions should install QBDI seamlessly.\n\n\nWindows\n+++++++\n\nA software installer is provided for Windows. Running the ``.exe`` and following the instructions\nshould install QBDI seamlessly.\n\n\nAndroid\n+++++++\n\nThe Android package is an archive you solely need to extract.\nAfterwards, you have to manually push the files onto the device.\n\nDevel packages\n++++++++++++++\n\nDevel packages embed the latest features the developers are currently working on for the next release (available on the `dev-next branch <https://github.com/QBDI/QBDI/tree/dev-next/>`_).\nIt's worth mentioning that since some parts are still under development, those are likely to be **unstable** -- you must be aware that they may contain some bugs and are not as reliable as release packages.\n\n- `Windows devel packages <https://ci.appveyor.com/project/QBDI/qbdi/branch/dev-next>`_\n- `Linux (Ubuntu) devel packages <https://github.com/QBDI/QBDI/actions?query=workflow%3A%22Tests+and+Package+Linux%22+branch%3Adev-next>`_\n- `Android devel packages <https://github.com/QBDI/QBDI/actions?query=workflow%3A%22Package+Android%22+branch%3Adev-next>`_\n- `macOS devel packages <https://github.com/QBDI/QBDI/actions?query=workflow%3A%22Tests+and+Package+macOS%22+branch%3Adev-next>`_\n\nPyQBDI installation\n-------------------\n\nEvery time a new stable release of PyQBDI is available, it is automatically pushed onto the `PyPI platform <https://pypi.org/project/PyQBDI/>`_, and can therefore be easily installed with ``pip`` (>= 19.3).\n\n.. code-block:: bash\n\n    pip install --user --upgrade pip\n    pip install --user PyQBDI\n\nIf you want to use a devel version, download the corresponding prebuilt wheel file and run the following commands:\n\n.. code-block:: bash\n\n    pip install --user --upgrade pip\n    pip install --user PyQBDI-*.whl\n\nThe devel wheel files which contain the latest versions of the `dev-next branch <https://github.com/QBDI/QBDI/tree/dev-next/>`_ are available at:\n\n- `PyQBDI for Windows <https://ci.appveyor.com/project/QBDI/qbdi/branch/dev-next>`_\n- `PyQBDI for Linux <https://github.com/QBDI/QBDI/actions?query=workflow%3A%22PyQBDI+Linux+package%22+branch%3Adev-next>`_\n- `PyQBDI for macOS <https://github.com/QBDI/QBDI/actions?query=workflow%3A%22PyQBDI+macOS+package%22+branch%3Adev-next>`_\n\n.. note::\n   Only Python3 is supported. If you need to use Python2, we recommend using QBDI 0.7.0 instead.\n\n.. note::\n   A 32-bit version of Python is needed if you want to use PyQBDI on x86 targets.\n\nFrida/QBDI installation\n-----------------------\n\nQBDI can be used alongside Frida to make it even more powerful. This feature is included in the C/C++ package.\nUsing it requires having Frida installed (>= 14.0) on your workstation as well as `frida-compile <https://www.npmjs.com/package/frida-compile>`_ for compiling your scripts.\n\n.. code-block:: bash\n\n    # install Frida\n    pip install frida-tools\n\n    # install frida-compile and add the binary directory to your PATH env variable\n    npm install frida-compile babelify\n    export PATH=$PATH:$(pwd)/node_modules/.bin\n\nAndroid target\n++++++++++++++\n\nIn order to use Frida/QBDI on an Android device, the Frida server must be running on the target device and the ``libQBDI.so`` library has to be placed in ``/data/local/tmp``.\n\n.. note::\n   Latest Frida server binaries are available on the `Frida official release page <https://github.com/frida/frida/releases>`_.\n\nDocker Linux images\n-------------------\n\nThe Docker image is available on `Docker Hub <https://hub.docker.com/r/qbdi/qbdi>`_.\nIt has been built to keep it as small as possible so it does not contain any compiler.\nYou have to install the needed application or modify the following Dockerfile according to your needs.\n\n.. literalinclude:: ../../examples/Dockerfile\n   :language: dockerfile\n\nTo run the container, we recommend allowing PTRACE usage, which is mandatory for QBDIPreload.\n\n.. code-block:: bash\n\n    docker run -it --rm --cap-add=SYS_PTRACE --security-opt seccomp:unconfined <image> bash\n\nCompilation from source code\n----------------------------\n\n.. include:: ../../README.rst\n    :start-after: .. compil\n    :end-before: .. compil-end\n\nCMake integration\n-----------------\n\nSingle architecture\n+++++++++++++++++++\n\nIf you want to use only one architecture of QBDI in your CMake project, you can import the ``QBDI`` and ``QBDIPreload`` packages:\n\n.. code-block:: cmake\n\n    find_package(QBDI REQUIRED)\n    find_package(QBDIPreload REQUIRED) # if available for your current platform\n    # or\n    find_package(QBDI REQUIRED HINTS \"${EXTRACT_DIRECTORY}\" NO_DEFAULT_PATH)\n    find_package(QBDIPreload REQUIRED HINTS \"${EXTRACT_DIRECTORY}\" NO_DEFAULT_PATH)\n\nOnce the CMake package is found, you can link your executable either with the dynamic or the static library:\n\n.. code-block:: cmake\n\n    add_executable(example example.c)\n\n    target_link_libraries(example QBDI::QBDI)\n    # or\n    target_link_libraries(example QBDI::QBDI_static)\n\n    add_executable(example_preload example_preload.c)\n    target_link_libraries(example_preload QBDI::QBDI_static QBDIPreload::QBDIPreload)\n\nMulti-architecture\n++++++++++++++++++\n\nIf two or more architectures of QBDI are needed within the same project, you should import the package ``QBDI<arch>`` and ``QBDIPreload<arch>``.\n\n.. code-block:: cmake\n\n    find_package(QBDIX86 REQUIRED HINTS \"${EXTRACT_DIRECTORY_X86}\" NO_DEFAULT_PATH)\n    find_package(QBDIPreloadX86 REQUIRED HINTS \"${EXTRACT_DIRECTORY_X86}\" NO_DEFAULT_PATH)\n\n    add_executable(example_preload86 example_preload.c)\n    set_target_properties(example_preload86 PROPERTIES COMPILE_FLAGS \"-m32\" LINK_FLAGS \"-m32\")\n    target_link_libraries(example_preload86 QBDI::X86::QBDI_static QBDIPreload::X86::QBDIPreload)\n\n    find_package(QBDIX86_64 REQUIRED HINTS \"${EXTRACT_DIRECTORY_X86_64}\" NO_DEFAULT_PATH)\n    find_package(QBDIPreloadX86_64 REQUIRED HINTS \"${EXTRACT_DIRECTORY_X86_64}\" NO_DEFAULT_PATH)\n\n    add_executable(example_preload64 example_preload.c)\n    target_link_libraries(example_preload64 QBDI::X86_64::QBDI_static QBDIPreload::X86_64::QBDIPreload)\n"
  },
  {
    "path": "docs/source/instrumentation_process.rst",
    "content": ".. _instrumentation_process:\n\nInstrumentation Process\n=======================\n\nIntroduction\n------------\n\n.. image:: images/api_architecture.svg\n\nThe Engine reads and disassembles the instrumented binary one basic block at a time. In case a\nconditional branch terminates a basic block, the execution result of this basic block is needed to\ndetermine the next basic block to execute. This makes the case for a per-basic-block\nprocessing and execution.\n\nEvery basic block is first patched to solve two main problems:\n\n* Relocation:\n    The basic block will be executed at a different location and thus every usage of the Program\n    Counter, either directly as an operand or indirectly when using relative memory addressing,\n    needs to be patched to make the code relocatable.\n* Control Flow:\n    Branching instructions should not be directly executed as this would result in the execution\n    escaping from the instrumentation process. Thus the resulting target of a branching instruction\n    needs to be computed without being taken.\n\nOnce a basic block has been patched, the instrumentation operations requested by the user code are applied.\nBoth the patching and the instrumentation are expressed in an *Embedded Domain Specific Language*\n[#dsl]_ called :doc:`/patchdsl` which is executed by the engine.\n\nThe resulting instrumented basic block is then handed over to the ExecBlockManager which handles\na cache of basic blocks placed inside execution units called :doc:`/execblock`. The ExecBlockManager is\ntasked with finding memory space inside an ExecBlock to place the instrumented basic block and also\nretrieving cached basic blocks.\n\nAn ExecBlock manages on the guest side two memory pages: one for the code, the code block, and one\nfor the data, the data block. The ExecBlock also handles the resolution of the relocation of the\npatched code before assembling it in the code block.\n\nThe instrumentation of the code allows defining callbacks to the user code directly from the\ninstrumented binary through the ExecBlock. These callbacks allow to inspect and modify the state\nof execution of the guest on the host side at every point in time.\n\n.. [#dsl] https://en.wikipedia.org/wiki/Domain-specific_language\n\n\nImplementation\n--------------\n\nThe figure below presents the *life of an instruction* and summarizes the main steps and classes\ninvolved along the way. This is intended to give an overview of what the internals do.\n\n.. image:: images/instruction_life.svg\n\nAn instruction exists in three different representations inside QBDI:\n\nBytes\n  Raw bytes of machine code in memory.\nMCInst\n  LLVM machine code representation. The instruction is only partially disassembled but still\n  provides a list of operands. Anyone interested in more details regarding this representation should\n  refer to the official LLVM documentation and experiment with `llvm-mc -show-inst`.\nRelocatableInst\n  QBDI representation of a relocatable MCInst. It consists of an MCInst and relocation information.\n\nThere is another important class: :cpp:class:`QBDI::Patch`. A :cpp:class:`QBDI::Patch` aggregates\nthe patch and the instrumentation of a single instruction in the form of a list of\n:cpp:class:`QBDI::RelocatableInst`. It is the smallest unit of code which can be assembled inside\nan :cpp:class:`QBDI::ExecBlock` as patching or instrumentation code cannot be split in parts\nwithout problematic side effects.\n\nThe assembly and disassembly steps are directly handled by LLVM for us. The Engine takes care of\nthe patching and instrumentation using a programmable list of :cpp:class:`QBDI::PatchRule` and\n:cpp:class:`QBDI::InstrRule`. More details on those rules can be found in the :doc:`/patchdsl`\nchapter. Relocation is handled directly in the :cpp:class:`QBDI::ExecBlock`.\n\n\n"
  },
  {
    "path": "docs/source/intro.rst",
    "content": ".. _user-introduction:\n\nIntroduction\n============\n\n.. role:: red\n\nWhy a DBI?\n-----------\n\nDebuggers are a popular approach to analyze the execution of a binary. While those tools are\nconvenient, they are also quite slow. This performance problem is imperceptible to human users but\nreally takes its toll on automated tools trying to single step through a complete program. Such\nautomated tools are useful for tracking the evolution of the program states, extracting execution\nstatistics and verifying that some runtime conditions hold true. Examples of usage include memory\ncorruption debuggers, profilers, timeless debuggers and side-channel attack tools.\n\nThis performance cost is due to the kernel playing the role of a middleman between the debugger and\nthe debuggee. The only way to get rid of the problem is to place the tool inside the binary being\nanalyzed and this is what Dynamic Binary Instrumentation does: injecting instrumentation code\ninside the binary at runtime.\n\n\nWhy QBDI?\n----------\n\nExisting DBI frameworks were designed more than 15 years ago, focusing on features and\nplatforms that made sense at the time. Mobile platform support is often unstable or nonexistent\nand instrumentation features are either simplistic or buried in low-level details.\n\nQBDI attempts to retain the interesting features of those frameworks while avoiding their pitfalls\nand bringing new designs and ideas. Its goal is to be a cross-platform and multi-architecture\nmodular DBI framework. The modular design exposes the DBI engine as a library that can start an\ninstrumented execution anywhere, anytime and easily be incorporated in other tools.\n\nQBDI : How does it work?\n-------------------------\n\nThe core of DBI frameworks relies on the Just-In-Time (JIT) recompilation of the original program.\nThis allows interleaving additional assembly code which can instrument any part of the execution.\nThe DBI engine performing the JIT recompilation and the JITed code itself run in the same process\nbut each need to have their own processor context. This requires performing context switches between\nthe two like a virtual machine would. We thus call the DBI context the **host** and the original\nprogram context the **guest**.\n\n.. image:: images/api_architecture_simple.svg\n\nThe **host** is composed of QBDI components and the **instrumentation tool**. The **instrumentation\ntool** is the code written by the user which interacts with the **QBDI VM** through a **C API** or\na **C++ API**. The **instrumentation tool** can register **callbacks** to occur on specific events\ntriggered either by QBDI or by the instrumentation code inserted inside the original program. The\nuser documentation further details these APIs and how callbacks work.\n\n\nInside the VM resides the **QBDI Engine** which manages the instrumented execution. The engine runs\nthe JIT loop which reads the **original program** code and generates the **instrumented code** which\nis then executed. Each loop iteration operates on a basic block, a sequence of instructions which\nends with a branching instruction. This basic block is first patched, to accommodate the JIT\nprocess, and then instrumented as instructed by the instrumentation tool. This instrumented basic\nblock is written in executable memory, executed and returns the address of the next basic block to\nexecute. To avoid doing the same work twice, this **instrumented code** is actually written in a\ncode cache.\n\n.. _intro_limitations:\n\nLimitations\n-----------\n\nThe **host** and the **guest** share the same process and thus the same resources. This means that\nthey use the same heap and the same libraries and this will cause issues with any non-reentrant\ncode. We could have chosen to shield users from those issues by forbidding instrumentation tools to\nuse any external libraries like some other DBI frameworks have done. However we believe this is\nan overblown issue and that there are effective mechanisms to mitigate the problem. Nonetheless\nusers need to be aware of this design limitation and the mitigations' side effects.\n\nFor example, tracing the heap memory allocator will cause deadlocks because it is not reentrant.\nThere are other problematic cases but they are mostly limited to the standard C library and the OS\nloader. To avoid such issues we have an execution brokering system that allows whitelisting/blacklisting\nspecific pieces of code. These will be executed outside of the instrumentation process via a call\nhooking mechanism. This execution broker system is documented in the :ref:`API description <instrumentation_range>`.\n\nMoreover, the **host** relies on the loader loading and initializing its library dependencies which\nmeans the instrumentation process cannot be started before the loader has finished its job. As a\nresult the loader itself cannot be instrumented.\n"
  },
  {
    "path": "docs/source/patchdsl.rst",
    "content": ".. highlight:: c++\n\nPatchDSL\n========\n\n.. contents::\n  :depth: 3\n\nLanguage Concepts\n-----------------\n\nType System\n+++++++++++\n\nThe PatchDSL nomenclature is formalized as a function of where information belongs using two\ndichotomies:\n\n* A physical dichotomy is made between data stored in the machine registers and data stored in\n  memory.\n* A conceptual dichotomy is made between data belonging or generated by the target program and data\n  belonging or generated by QBDI.\n\nThis creates four main categories of data being manipulated by the PatchDSL. These categories and\nthe nomenclature for their interaction are represented below.\n\n.. image:: images/patchdsl_concepts.svg\n\nReg:\n  They represent machine registers storing data created and used by the target program.\nTemp:\n  They represent machine registers storing data used by the instrumentation. Those are temporary\n  scratch registers which were allocated by saving a Reg into the context and are bound to be\n  deallocated by restoring the Reg value from the context.\nContext:\n  The context stores in memory the processor state associated with the target program. It is mostly\n  used for context switching between the target program and the instrumentation process and also\n  for allocating temporary registers.\nShadows:\n  They represent shadow data associated with a patch and an instrumented instruction. They can be\n  used by QBDI to store constants or Tagged Shadows.\nMetadata:\n  Any data regarding the execution which can be generated by QBDI. For example, obtaining\n  instruction operand values or memory access addresses.\n\nThe main objective of this strict naming convention is to make the distinction between pure (no side effects) operations and operations affecting the program state as clear as possible. To make this\ndistinction even more apparent the DSL is strongly typed forcing variables to be declared by\nallocating one of the typed structures below with its constructor. Because some of those structures\nsimply alias an integer value it can be tempting to directly use the integer value, letting the\ncompiler do the implicit type conversion. However the point of those structures is to give a\ncontext to what those integer constants represent and the best practice is to use them\neverywhere possible.\n\n.. doxygenstruct:: QBDI::Reg\n  :members:\n\n.. doxygenstruct:: QBDI::Temp\n  :members:\n\n.. doxygenstruct:: QBDI::Shadow\n  :members:\n\n.. doxygenstruct:: QBDI::Constant\n  :members:\n\n.. doxygenstruct:: QBDI::Offset\n  :members:\n\n.. doxygenstruct:: QBDI::Operand\n  :members:\n\nStatements\n++++++++++\n\nThere are three main categories of statements composing PatchDSL, each characterized by different\nvirtual base classes. The specializations of those base classes are PatchDSL statements.\n\n:cpp:class:`QBDI::PatchCondition`\n  They are used to match specific instructions. They take the instruction and its context as an\n  input and return a boolean.\n:cpp:class:`QBDI::PatchGenerator`\n  They represent operations generating new instructions. They take the instruction and its context\n  as an input and return a list of :cpp:class:`QBDI::RelocatableInst` constituting the patch. In\n  some exceptional cases no output is generated.\n:cpp:class:`QBDI::InstTransform`\n  They represent operations transforming an instruction. They only manipulate an instruction and\n  need to be used with a :cpp:class:`QBDI::PatchGenerator` to output a\n  :cpp:class:`QBDI::RelocatableInst`.\n\nThose statements are all evaluated on an implicit context. In the case of\n:cpp:class:`QBDI::InstTransform` the context is the instruction to modify which is determined by\nthe :cpp:class:`QBDI::PatchGenerator` which uses it. In the case of :cpp:class:`QBDI::PatchCondition`\nand :cpp:class:`QBDI::PatchGenerator` this context is made of:\n\n* the current instruction\n* the current instruction size\n* the current address\n\nThe output of each statement thus depends on the statement parameters and this implicit context.\n\nRules\n+++++\n\nPatchDSL is used to write short sequences of statements called *rules*. There exists two variants of\nrules, patching rules (:cpp:class:`QBDI::PatchRule`) and instrumentation rules\n(:cpp:class:`QBDI::InstrRule`), but they both rely on the same principle. A rule is composed of\ntwo parts:\n\nCondition:\n  A :cpp:class:`QBDI::PatchCondition` statement which expresses the condition under which the rule\n  should be applied. Multiple statements can be combined in a boolean expression using\n  :cpp:class:`QBDI::Or` and :cpp:class:`QBDI::And`. If the evaluation of this expression returns\n  ``true`` then the generation part of the rule is evaluated.\nGeneration:\n  A list of :cpp:class:`QBDI::PatchGenerator` statements which will generate the patch rule. Each\n  statement can output one or several :cpp:class:`QBDI::RelocatableInst`, the resulting patch being\n  the aggregation of all those statement outputs.\n\n.. doxygenclass:: QBDI::PatchRule\n  :members:\n\n.. doxygenclass:: QBDI::InstrRule\n  :members:\n\nTransforms\n++++++++++\n\nTransform statements, with the :cpp:class:`QBDI::InstTransform` virtual base class, are a bit more\nsubtle than other statements.\n\nCurrently their operation is limited to the :cpp:class:`QBDI::ModifyInstruction` generators which\nalways operate on the instruction of the implicit context of a patch or instrumentation rule.\nHowever their usage could be extended in the future.\n\nTheir purpose is to allow to write more generic rules by allowing modifications which can operate\non a class of instructions. Using instruction transforms requires to understand the underlying\nLLVM MCInst representation of an instruction and ``llvm-mc -show-inst`` is a helpful tool for this\ntask.\n\nPatchDSL Examples\n-----------------\n\nBelow some real examples of patch and instrumentation rules are shown.\n\nBasic Patching\n++++++++++++++\n\nGeneric PC Substitution Patch Rule\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nInstructions using the Program Counter (PC) in their computations are problematic because QBDI will\nreassemble and execute the code at another address than the original code location and thus the\nvalue of the PC will change. This kind of computation using the PC is often found when using\nrelative memory addressing.\n\nSome cases can be more difficult to handle, but most of these instructions can be patched using a\nvery simple generic rule performing the following steps:\n\n1. Allocate a scratch register by saving a register value in the context part of the data block.\n2. Load the value the PC register should have, into the scratch register.\n3. Perform the original instruction but with PC replaced by the scratch register.\n4. Deallocate the scratch register by restoring the register value from the context part of the data\n   block.\n\nThe PatchDSL :cpp:class:`QBDI::PatchRule` handles step 1 and 4 automatically for us. Expressing\nstep 2 and 3 is relatively simple::\n\n    PatchRule(\n        // Condition: Applies on every instruction using the register REG_PC\n        UseReg(Reg(REG_PC)),\n        // Generators: list of statements generating the patch\n        {\n            // Compute PC + 0 and store it in a new temp with id 0\n            GetPCOffset(Temp(0), Constant(0)),\n            // Modify the instruction by substituting REG_PC with the temp having id 0\n            ModifyInstruction({\n                SubstituteWithTemp(Reg(REG_PC), Temp(0))\n            })\n        }\n    )\n\nThis rule is generic and works under X86_64 as well as ARM. Some more complex cases of\ninstructions using PC need to be handled another way though.\n\nSimple Branching Instruction Patch\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAnother simple case which needs to be handled using a patch rule is branching instructions. They\ncannot be executed because that would mean the DBI process would lose control of execution.\nInstead of executing the branch operation, the branch target is computed and used to overwrite the\nvalue of the PC in the context part of the data block. This is followed by a context switch back\nto the VM which will use this target as the address where to continue the execution.\n\nThe simplest cases are the \"branch to an address stored in a register\" instructions. Again the\ntemporary register allocation is automatically taken care of by the :cpp:class:`QBDI::PatchRule` and\nwe only need to write the patching logic::\n\n    PatchRule(\n        // Condition: only on BX or BX_pred LLVM MCInst\n        Or({\n            OpIs(llvm::ARM::BX),\n            OpIs(llvm::ARM::BX_pred)\n        }),\n        // Generators\n        {\n            // Obtain the value of the operand with index 0 and store it in a new temp with id 0\n            GetOperand(Temp(0), Operand(0)),\n            // Write the temp with id 0 at the offset in the data block of the context value of REG_PC.\n            WriteTemp(Temp(0), Offset(Reg(REG_PC)))\n        }\n    )\n\nTwo things are important to notice here. First we use :cpp:class:`QBDI::Or` to combine multiple\n:cpp:class:`QBDI::PatchCondition`. Second the fact we need to stop the execution here and switch\nback to the context of the VM is not expressed in the patch. Indeed the patching engine simply\nnotices that this patch overwrites the value of the PC and thus needs to end the basic block after\nit.\n\nAdvanced Patching\n+++++++++++++++++\n\nConditional Branching Instruction Patch\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe previous section dealt with simple patching cases where the rule does not need to be very complex.\nConditional instructions can add a significant amount of complexity to the writing of patch\nrules and require some tricks. Below is the patch for the ARM conditional branching instruction::\n\n    PatchRule(\n        // Condition: every Bcc instructions (e.g. BNE, BEQ, etc.)\n        OpIs(llvm::ARM::Bcc),\n        // Generators\n        {\n            // Compute the Bcc target (which is PC relative) and store it in a new temp with id 0\n            GetPCOffset(Temp(0), Operand(0)),\n            // Modify the jump target such as it potentially skips the next generator\n            ModifyInstruction({\n                SetOperand(Operand(0), Constant(0))\n            }),\n            // Compute the next instruction address and store it in temp with id 0\n            GetPCOffset(Temp(0), Constant(-4)),\n            // At this point either:\n            //  * The jump was not taken and Temp(0) stores the next instruction address.\n            //  * The jump was taken and Temp(0) stores the Bcc target\n            // We thus write Temp(0) which has the correct next address to execute in the REG_PC\n            // value in the context part of the data block.\n            WriteTemp(Temp(0), Offset(Reg(REG_PC)))\n        }\n    )\n\nAs we can see, this code reuses the original conditional branching instruction to create a\nconditional move. While this is a trick, it is an architecture independent trick which is also\nused under X86_64. Some details can be noted though. First the next instruction address is PC - 4\nwhich is an ARM specificity. Secondly, the constant used to overwrite the jump target needs to\nbe determined by hand as QBDI does not have the capacity to compute it automatically.\n\nComplex InstTransform\n^^^^^^^^^^^^^^^^^^^^^\n\nThe patch below is used to patch instructions which load their branching target from a memory\naddress under X86_64. It exploits :cpp:class:`QBDI::InstTransform` to convert the instruction\ninto a load from memory to obtain this branching target::\n\n    PatchRule(\n        // Condition: applies on CALL where the target is at a relative memory location (thus uses REG_PC)\n        And({\n            OpIs(llvm::X86::CALL64m),\n            UseReg(Reg(REG_PC))\n        }),\n        // Generators\n        {\n            // First compute PC + 0 and stores it into a new temp with id 0\n            GetPCOffset(Temp(0), Constant(0)),\n            // Transforms the CALL *[RIP + ...] into MOV Temp(1), *[Temp(0) + ...]\n            ModifyInstruction({\n                // RIP is replaced with Temp(0)\n                SubstituteWithTemp(Reg(REG_PC), Temp(0)),\n                // The opcode is changed to a 64-bit MOV from memory to a register\n                SetOpcode(llvm::X86::MOV64rm),\n                // We insert the destination register, a new temp with id 1,  at the beginning of\n                // the operand list\n                AddOperand(Operand(0), Temp(1))\n            }),\n            // Temp(1) thus contains the CALL target.\n            // We use the X86_64 specific SimulateCall with this target.\n            SimulateCall(Temp(1))\n        }\n    )\n\nA few things need to be noted. First the sequence of :cpp:class:`QBDI::InstTransform` is complex\nbecause it substitutes RIP and it mutates the CALL into a MOV. Secondly, new :cpp:class:`QBDI::Temp`\ncan be instantiated and used anywhere in the program. Lastly, some complex architecture specific\nmechanisms have been abstracted in single :cpp:class:`QBDI::PatchGenerator`, like\n:cpp:class:`QBDI::SimulateCall`.\n\nInstrumentation Callbacks\n+++++++++++++++++++++++++\n\n:cpp:class:`QBDI::InstrRule` allows inserting inline instrumentation inside the patch with a concept\nsimilar to the rules shown previously. Callbacks to host code are triggered by a break to host with\nspecific variables set correctly in the host state part of the context:\n\n* the hostState.callback should be set to the callback function address to call.\n* the hostState.data should be set to the callback function data parameter.\n* the hostState.origin should be set to the ID of the current instruction (see :cpp:class:`QBDI::GetInstId`).\n\nIn practice, there exists a function which can generate the PatchGenerator needed to setup those\nvariables correctly:\n\n.. doxygenfunction:: QBDI::getCallbackGenerator\n\nThus, in practice, a :cpp:class:`QBDI::InstrRule` which would set up a callback on every\ninstruction writing data in memory would look like this::\n\n    InstrRule(\n        // Condition: on every instruction making write access\n        DoesWriteAccess(),\n        // Generators: set up a callback to someCallbackFunction with someParameter\n        getCallbackGenerator(someCallbackFunction, someParameter),\n        // Position this instrumentation after the instruction\n        InstPosition::POSTINST,\n        // Break to the host after the instrumentation (required for the callback to be made)\n        true\n    ));\n\nHowever the callback generator can be written directly in PatchDSL for more advantageous usages. The\ninstrumentation rules below pass directly the written data as the callback parameter::\n\n    InstrRule(\n        // Condition: on every instruction making write access\n        DoesWriteAccess(),\n        // Generators: set up a callback to someCallbackFunction with someParameter\n        {\n            // Set hostState.callback to the callback function address\n            GetConstant(Temp(0), Constant((rword) someCallbackFunction)),\n            WriteTemp(Temp(0), Offset(offsetof(Context, hostState.callback))),\n            // Set hostState.data as the written value\n            GetWriteValue(Temp(0)),\n            WriteTemp(Temp(0), Offset(offsetof(Context, hostState.data))),\n            // Set hostState.origin as the current instID\n            GetInstId(Temp(0)),\n            WriteTemp(Temp(0), Offset(offsetof(Context, hostState.origin)))\n        },\n        // Position this instrumentation after the instruction\n        QBDI::InstPosition::POSTINST,\n        // Break to the host after the instrumentation (required for the callback to be made)\n        true\n    ));\n\n"
  },
  {
    "path": "docs/source/references.rst",
    "content": "References\n==========\n\nConferences and Workshops\n-------------------------\n\n- 2020-10-15: BSides Delhi - Analysing programs through dynamic instrumentation with QBDI by Tom Czayka and Nicolas Surbayrole\n- 2020-06-29: `Pass The Salt - Why are Frida and QBDI a Great Blend on Android? <https://passthesalt.ubicast.tv/videos/2020-why-are-frida-and-qbdi-a-great-blend-on-android/>`_ by Tom Czayka\n- 2019-04-23: `French-Japan cybersecurity workshop - Fuzzing binaries using Dynamic Instrumentation <https://project.inria.fr/FranceJapanICST/files/2019/04/19-Kyoto-Fuzzing_Binaries_using_Dynamic_Instrumentation.pdf>`_ by Paul Hernault\n- 2017-12-28: `34C3 conferences -  Implementing an LLVM based Dynamic Binary Instrumentation framework <https://media.ccc.de/v/34c3-9006-implementing_an_llvm_based_dynamic_binary_instrumentation_framework>`_ by Charles Hubain and Cédric Tessier\n\n\nBlog Post\n---------\n\n- 2020-08-18: `Introduction to Whiteboxes and Collision-Based Attacks With QBDI <https://blog.quarkslab.com/introduction-to-whiteboxes-and-collision-based-attacks-with-qbdi.html>`_ by Paul Hernault\n- 2020-08-04: `Why are Frida and QBDI a Great Blend on Android? <https://blog.quarkslab.com/why-are-frida-and-qbdi-a-great-blend-on-android.html>`_ by Tom Czayka\n- 2019-09-10: `QBDI 0.7.0 <https://blog.quarkslab.com/qbdi-070.html>`_\n- 2019-06-03: `Android Native Library Analysis with QBDI <https://blog.quarkslab.com/android-native-library-analysis-with-qbdi.html>`_ by Romain Thomas\n- 2018-01-24: `Slaying Dragons with QBDI <https://blog.quarkslab.com/slaying-dragons-with-qbdi.html>`_ by Paul Hernault\n"
  },
  {
    "path": "docs/source/repo_organization.rst",
    "content": "Repository Organization\n=======================\n\nRoot Tree\n---------\n\nThe root of the source tree is organized as follows:\n\n``cmake/``\n   Contains files required by the CMake build system.\n\n``docker/``\n   Contains the Dockerfile for the docker image and the CI.\n\n``docs/``\n   Contains this documentation source and configuration.\n\n``examples/``\n   Contains various QBDI usage examples.\n\n``include/``\n   Contains the public include files.\n\n``package/``\n   Contains the package generation scripts.\n\n``src/``\n   Contains QBDI source code. This tree is described further in section :ref:`source-tree`.\n\n``templates/``\n   Contains QBDI usage templates.\n\n``test/``\n   Contains the functional test suite.\n\n``third-party/``\n   Contains the third party dependency downloaded by cmake.\n\n``tools/``\n   Contains QBDI development tools: the validator and the validation runner.\n\n.. _source-tree:\n\nSource Tree\n-----------\n\nThe source files are organized as follows:\n\n``src/Engine``\n   Contains code related to the VM API and underlying Engine controlling the overall execution.\n\n``src/ExecBlock``\n   Contains code used to generate, store and execute JITed code inside ExecBlocks which form a code\n   cache managed by the ExecBlockManager.\n\n``src/ExecBroker``\n   Contains code implementing the execution brokering mechanism allowing to switch between\n   instrumented execution and real execution.\n\n``src/Patch``\n   Contains the PatchDSL implementation and per architecture support.\n\n``src/Utility``\n   Contains various utility class and functions.\n"
  },
  {
    "path": "docs/source/techref.rst",
    "content": "Technical Reference\n===================\n\n.. toctree::\n   :maxdepth: 2\n\n   Instrumentation Process <instrumentation_process>\n   ExecBlock               <execblock>\n   PatchDSL                <patchdsl>\n"
  },
  {
    "path": "docs/source/testing.rst",
    "content": ".. _developer-testing:\n\nTesting\n=======\n\nMaking mistakes while writing a DBI framework is particularly easy and often fatal. Executing\ncode generated at runtime is a dangerous game and close attention should be paid to the patching\nengine. That is why we use two different testing methods.\n\nFunctional Test Suite\n---------------------\n\nQBDI has a small functional test suite implemented using `Catch2 <https://github.com/catchorg/Catch2>`_\nwhich verifies the essential functions of QBDI. This test suite is automatically built and made\navailable in the ``test/`` build subdirectory::\n\n    $ ./test/QBDITest\n    ===============================================================================\n    All tests passed (110977 assertions in 106 test cases)\n\n\n\nValidator\n---------\n\nThe validator is a generic test system allowing to compare a normal execution with an instrumented\nexecution. This way a large number of programs can be used as a test suite to ensure that QBDI does\nnot alter their normal functions. It is only compatible with Linux and macOS at the moment.\n\nThe idea is to pilot a debugging session using an instrumented execution of the same program. These\ntwo instances share the same environment and arguments and originated from the same fork thus have\nthe same memory layout. This allows comparing the program state at each step and verify that both\nexecutions take the same path and give the same result. The validator is not only capable of\ndetermining if two executions differ but also capable of identifying where they diverged. It thus\ndoubles as a debugging tool.\n\nThere are, however, a few caveats to this approach. First, the two instances of the program will\ncompete for resources. This means running ``sha1sum test.txt`` will work because the two instances\ncan read the same file at the same time, but removing a directory ``rmdir testdir/`` will always fail because\nonly one instance will be able to delete the directory. Second, the dynamic memory allocations will\nnot match on the two instances. This is because the instrumented instance is running the whole QBDI\nframework and validator instrumentation which is making extra allocations in between the original\nprogram allocations. A partial mitigation was nevertheless implemented which tracks allocations on both\nsides and computes an allowed offset for specific memory address ranges.\n\nOne of the essential concepts of the validator is error cascades. They establish a probable\ncausality chain between errors and allow to backtrack from the point where the execution crashed\nor diverged to the probable cause. Indeed, an error might only cause problems thousands of\ninstructions later.\n\nThe validator uses dynamic library injection to take control of a program startup. Options are\ncommunicated using environment variables. The following options are available:\n\n``VALIDATOR_VERBOSITY``\n    * ``Stat``: Only display execution statistics. This is the default.\n    * ``Summary``: Display execution statistics and error cascade summaries.\n    * ``Detail``: Display execution statistics and complete error cascades.\n    * ``Full``: Display full execution trace, execution statistics and complete error cascades.\n\n``VALIDATOR_COVERAGE``\n    Specify a file name where instruction coverage statistics will be written out.\n\nLinux\n^^^^^\n\nThe validator uses ``LD_PRELOAD`` for injection under Linux and an example command line would be::\n\n    $ LD_PRELOAD=./tools/validator/libvalidator.so VALIDATOR_VERBOSITY=Detail VALIDATOR_COVERAGE=coverage.txt ls\n\nmacOS\n^^^^^\n\nThe validator uses ``DYLD_INSERT_LIBRARIES`` for injection under macOS and an example command line would be::\n\n    $ DYLD_INSERT_LIBRARIES=./tools/validator/libvalidator.dylib VALIDATOR_VERBOSITY=Detail VALIDATOR_COVERAGE=coverage.txt ./ls\n\nPlease note that, under macOS, SIP prevents from debugging system binaries. A workaround is to copy\nthe target binary in a local directory. Also, for the moment, the validator requires root to\nobtain debugging rights.\n\nValidation Runner\n-----------------\n\nThe validation runner is an automation system to run a series of validation tasks specified in a\nconfiguration file and aggregate the results. Those results are stored in a database which enables\nhistorical comparisons between validation runs and warns in case of an anomaly.\n"
  },
  {
    "path": "docs/source/tutorial.rst",
    "content": "\nTutorials\n=========\n\nThis section contains some tutorials on specifics points of QBDI\n\n\n.. toctree::\n    :maxdepth: 2\n\n    BasicBlock VMEvent <tutorial_BBVMEvent>\n    Execution transfert event <tutorial_ExecBrokerEvent>\n\n"
  },
  {
    "path": "docs/source/tutorial_BBVMEvent.rst",
    "content": ".. currentmodule:: pyqbdi\n\nBasic block events\n==================\n\nIntroduction\n------------\n\nThe :ref:`Instrument Callback <api_desc_InstCallback>` can insert callbacks on all or specific instructions.\nWith a callback on every instruction, it's trivial to follow the execution pointer and obtain a trace of the execution.\nHowever, performances are bad because the execution is stopped on each instruction for the callback to be run.\nFor some traces, a higher-level callback may have better performances.\n\nThe :ref:`api_desc_VMCallback` is called when some conditions are reached during the execution. This tutorial\nintroduces 3 `VMEvent`:\n\n- ``BASIC_BLOCK_NEW`` is triggered when a new basic block has been instrumented and added to the cache. It can be used to create\n  coverage of the execution.\n- ``BASIC_BLOCK_ENTRY`` is triggered before the execution of a basic block.\n- ``BASIC_BLOCK_EXIT`` is triggered after the execution of a basic block.\n\nA basic block in QBDI\n---------------------\n\nQBDI doesn't analyze the whole program before the run. Basic blocks are dynamically detected and so may not match basic blocks given by other tools.\nIn QBDI, a basic block is a sequence of consecutive instructions that do not modify the instruction pointer except for the last one.\nAny instruction that may modify the instruction pointer (method call, jump, conditional jump, method return, ...) is always the\nlast instruction of a basic block.\n\nFor QBDI, the beginning of a basic block is one of the following:\n\n- the very first instruction to be executed in QBDI;\n- the first instruction to be executed after the end of the previous basic block;\n- the first instruction to be executed if the user modifies the execution flow (add a new callback, clear the cache, return ``BREAK_TO_VM``, ...)\n\nDue to the dynamic detection of the basic block, basic blocks may overlap each other.\nThis behavior can be observed in the following code:\n\n.. code:: nasm\n\n    # BB\n       push rbp                          # 0x1000\n       mov  rbp, rsp                     # 0x1001\n       mov  dword ptr [rbp - 0x14], edi  # 0x1004\n       mov  edx, dword ptr [rbp - 0x14]  # 0x1007\n       mov  eax, edx                     # 0x100a\n       shl  eax, 2                       # 0x100c\n       add  eax, edx                     # 0x100f\n       mov  dword ptr [rbp - 4], eax     # 0x1011\n       cmp  dword ptr [rbp - 0x14], 0xa  # 0x1014\n       jle  0x1027                       # 0x1018\n    # BB\n       add  dword ptr [rbp - 4], 0x33    # 0x101e\n       jmp  0x1033                       # 0x1022\n    # BB\n       mov  eax, dword ptr [rbp - 4]     # 0x1027\n       imul eax, eax                     # 0x102a\n       add  eax, 0x57                    # 0x102d\n       mov  dword ptr [rbp - 4], eax     # 0x1030\n    # BB\n       mov  edx, dword ptr [rbp - 4]     # 0x1033\n       mov  eax, dword ptr [rbp - 0x14]  # 0x1036\n       add  eax, edx                     # 0x1039\n       pop  rbp                          # 0x103b\n       ret                               # 0x103c\n\nIn this snippet, QBDI can detect 4 different basic blocks. If the first jump isn't taken:\n\n- The beginning of the method, between 0x1000 and 0x101e;\n- The block between 0x101e and 0x1027;\n- The last block between 0x1033 and 0x103d.\n\nIf the first jump is taken:\n\n- The beginning of the method, between 0x1000 and 0x101e;\n- The last block between 0x1027 and 0x103d.\n\n\nGetting basic block information\n-------------------------------\n\nTo receive basic block information, a ``VMCallback`` should be registered to the VM with ``addVMEventCB`` for\none of ``BASIC_BLOCK_*`` events. Once a registered event occurs, the callback is run with a description of the VM (``VMState``).\n\nThe address of the current basic block can be retrieved with ``VMState.basicBlockStart`` and ``VMState.basicBlockEnd``.\n\n.. note::\n\n    A callback may register for both ``BASIC_BLOCK_NEW`` and ``BASIC_BLOCK_ENTRY`` events, but would be called only once if these two events happen at the same time.\n    You can retrieve the events that triggered the callback in ``VMState.event``.\n\nThe following example registers for the three events in the VM and displays the basic block's bounds.\n\nC basic block information\n+++++++++++++++++++++++++\n\nReference: :cpp:type:`VMCallback`, :cpp:func:`qbdi_addVMEventCB`, :cpp:struct:`VMState`\n\n.. code:: c\n\n    VMAction vmcbk(VMInstanceRef vm, const VMState* vmState, GPRState* gprState, FPRState* fprState, void* data) {\n\n        printf(\"start:0x%\" PRIRWORD \", end:0x%\" PRIRWORD \"%s%s%s\\n\",\n            vmState->basicBlockStart,\n            vmState->basicBlockEnd,\n            (vmState->event & QBDI_BASIC_BLOCK_NEW)? \" BASIC_BLOCK_NEW\":\"\",\n            (vmState->event & QBDI_BASIC_BLOCK_ENTRY)? \" BASIC_BLOCK_ENTRY\":\"\",\n            (vmState->event & QBDI_BASIC_BLOCK_EXIT)? \" BASIC_BLOCK_EXIT\":\"\");\n        return QBDI_CONTINUE;\n    }\n\n    qbdi_addVMEventCB(vm, QBDI_BASIC_BLOCK_NEW | QBDI_BASIC_BLOCK_ENTRY | QBDI_BASIC_BLOCK_EXIT, vmcbk, NULL);\n\nC++ basic block information\n+++++++++++++++++++++++++++\n\nReference: :cpp:type:`QBDI::VMCallback`, :cpp:func:`QBDI::VM::addVMEventCB`, :cpp:struct:`QBDI::VMState`\n\n.. code:: cpp\n\n    QBDI::VMAction vmcbk(QBDI::VMInstanceRef vm, const QBDI::VMState* vmState, QBDI::GPRState* gprState, QBDI::FPRState* fprState, void* data) {\n\n        std::cout << std::setbase(16) << \"start:0x\" << vmState->basicBlockStart\n                  << \", end:0x\" << vmState->basicBlockEnd;\n        if (vmState->event & QBDI::BASIC_BLOCK_NEW) {\n            std::cout << \" BASIC_BLOCK_NEW\";\n        }\n        if (vmState->event & QBDI::BASIC_BLOCK_ENTRY) {\n            std::cout << \" BASIC_BLOCK_ENTRY\";\n        }\n        if (vmState->event & QBDI::BASIC_BLOCK_EXIT) {\n            std::cout << \" BASIC_BLOCK_EXIT\";\n        }\n        std::cout << std::endl;\n        return QBDI::CONTINUE;\n    }\n\n    vm.addVMEventCB(QBDI::BASIC_BLOCK_NEW | QBDI::BASIC_BLOCK_ENTRY | QBDI::BASIC_BLOCK_EXIT, vmcbk, nullptr);\n\nPyQBDI basic block information\n++++++++++++++++++++++++++++++\n\nReference: :py:func:`pyqbdi.VMCallback`, :py:func:`pyqbdi.VM.addVMEventCB`, :py:class:`pyqbdi.VMState`\n\n.. code:: python\n\n    def vmcbk(vm, vmState, gpr, fpr, data):\n        # user callback code\n\n        print(\"start:0x{:x}, end:0x{:x} {}\".format(\n            vmState.basicBlockStart,\n            vmState.basicBlockEnd,\n            vmState.event & (pyqbdi.BASIC_BLOCK_NEW | pyqbdi.BASIC_BLOCK_ENTRY | pyqbdi.BASIC_BLOCK_EXIT) ))\n        return pyqbdi.CONTINUE\n\n    vm.addVMEventCB(pyqbdi.BASIC_BLOCK_NEW | pyqbdi.BASIC_BLOCK_ENTRY | pyqbdi.BASIC_BLOCK_EXIT, vmcbk, None)\n\nFrida/QBDI basic block information\n++++++++++++++++++++++++++++++++++\n\nReference: :js:func:`VMCallback`, :js:func:`VM.addVMEventCB`, :js:class:`VMState`\n\n.. code:: js\n\n    var vmcbk = vm.newVMCallback(function(vm, state, gpr, fpr, data) {\n        var msg = \"start:0x\" + state.basicBlockStart.toString(16) + \", end:0x\" + state.basicBlockEnd.toString(16);\n        if (state.event & VMEvent.BASIC_BLOCK_NEW) {\n            msg = msg + \" BASIC_BLOCK_NEW\";\n        }\n        if (state.event & VMEvent.BASIC_BLOCK_ENTRY) {\n            msg = msg + \" BASIC_BLOCK_ENTRY\";\n        }\n        if (state.event & VMEvent.BASIC_BLOCK_EXIT) {\n            msg = msg + \" BASIC_BLOCK_EXIT\";\n        }\n        console.log(msg);\n        return VMAction.CONTINUE;\n    });\n\n    vm.addVMEventCB(VMEvent.BASIC_BLOCK_NEW | VMEvent.BASIC_BLOCK_ENTRY | VMEvent.BASIC_BLOCK_EXIT, vmcbk, null);\n\n\nBasic block coverage\n--------------------\n\nTo perform code coverage, ``BASIC_BLOCK_NEW`` can be used to detect the new basic block JITed by QBDI.\nHowever, it wouldn't work in the following cases:\n\n- If the code jumps outside of the instrumented range.\n- If the code triggers an interruption (exception, signal, ...)\n- If the code uses overlapping instructions or other forms of obfuscation.\n\nMoreover, if a VM is reused from an execution to another, the cache will be kept\nand so coverage would be incremental. Clear the cache between every run to have\nindependent coverage results.\n\nFor more precise coverage, a user may register ``BASIC_BLOCK_ENTRY`` or ``BASIC_BLOCK_EXIT`` events and handle deduplication themselves.\n\n\nC coverage\n++++++++++\n\n.. code:: c\n\n    // your own coverage library\n    #include \"mycoverage.h\"\n\n    VMAction covcbk(VMInstanceRef vm, const VMState* vmState, GPRState* gprState, FPRState* fprState, void* data) {\n\n        myCoverageAdd( (myCoverageData*) data, vmState->basicBlockStart, vmState->basicBlockEnd);\n        return QBDI_CONTINUE;\n    }\n\n    myCoverageData cover;\n\n    qbdi_addVMEventCB(vm, QBDI_BASIC_BLOCK_NEW, covcbk, &cover);\n\n    // run the VM\n    // ....\n\n    // print the coverage\n    myCoveragePrint(&cover);\n\n\nC++ coverage\n++++++++++++\n\nQBDI has a tiny range set class (:cpp:class:`QBDI::RangeSet`), usable only with the C++ API.\n\n.. code:: cpp\n\n    QBDI::VMAction covcbk(QBDI::VMInstanceRef vm, const QBDI::VMState* vmState, QBDI::GPRState* gprState, QBDI::FPRState* fprState, void* data) {\n\n        QBDI::RangeSet<QBDI::rword>* rset = static_cast<QBDI::RangeSet<QBDI::rword>*>(data);\n        rset->add({vmState->basicBlockStart, vmState->basicBlockEnd});\n\n        return QBDI::CONTINUE;\n    }\n\n    QBDI::RangeSet<QBDI::rword> rset;\n\n    vm.addVMEventCB(QBDI::BASIC_BLOCK_NEW, covcbk, &rset);\n\n    // run the VM\n    // ....\n\n    // print the coverage\n    for (const auto &r: rset.getRanges()) {\n        std::cout << std::setbase(16) << \"0x\" << r.start() << \" to 0x\" << r.end() << std::endl;\n    }\n\nPyQBDI coverage\n+++++++++++++++\n\n.. code:: python\n\n    def covcbk(vm, vmState, gpr, fpr, data):\n\n        if vmState.basicBlockEnd not in data['cov'] or vmState.basicBlockStart < data['cov'][vmState.basicBlockEnd][0]:\n            data['cov'][vmState.basicBlockEnd] = (vmState.basicBlockStart, vmState.basicBlockEnd)\n        return pyqbdi.CONTINUE\n\n\n    cov = {\"cov\": {}}\n\n    vm.addVMEventCB(pyqbdi.BASIC_BLOCK_NEW, covcbk, cov)\n\n    # run the VM\n    # ....\n\n    for _, c in cov['cov'].items():\n        print(f\"0x{c[0]:x} to 0x{c[1]:x}\")\n\nIn addition, a coverage script that generates DRCOV coverage is available in `examples/pyqbdi/coverage.py <https://github.com/QBDI/QBDI/blob/master/examples/pyqbdi/coverage.py>`_.\n\n\nFrida/QBDI coverage\n+++++++++++++++++++\n\n.. code:: js\n\n    var covcbk = vm.newVMCallback(function(vm, state, gpr, fpr, cov) {\n        if ( (! cov[state.basicBlockEnd]) || state.basicBlockStart < cov[state.basicBlockEnd][0] ) {\n            cov[state.basicBlockEnd] = [state.basicBlockStart, state.basicBlockEnd]\n        }\n        return VMAction.CONTINUE;\n    });\n\n    var cov = {};\n\n    vm.addVMEventCB(VMEvent.BASIC_BLOCK_NEW, covcbk, cov);\n\n    // run the VM\n    // ....\n\n    for(var c in cov){\n        console.log(\"0x\" + cov[c][0].toString(16) + \" to 0x\" + cov[c][1].toString(16));\n    }\n\nEdge coverage\n-------------\n\nThe ``BASIC_BLOCK_EXIT`` event can be used to detect the edge between basic blocks. As the event is triggered at the end of a basic block (i.e., after the instruction pointer is modified),\nthe next address can be found in the GPRState. So, the couple ``(state.basicBlockEnd, gpr.rip)`` is the edge to store in the coverage.\n"
  },
  {
    "path": "docs/source/tutorial_ExecBrokerEvent.rst",
    "content": "Execution transfer event\n=========================\n\nIntroduction\n------------\n\nOne limitation of QBDI is that it shares the heap and some library with the instrumented code.\nWith this design, the user may use any shared library and doesn't need to statically link all their\ndependencies with their code. However, some method must not be instrumented in QBDI:\n\n- The heap allocator method (malloc, free, ...).\n- Any non-reentrant method shared between the target code and QBDI itself.\n- Any non-reentrant method shared between the target code and user callbacks.\n\nWhen the target code calls one of these methods, QBDI restores the native execution.\nThe return address is changed in order to catch the return and to continue the instrumentation of the\ncode. Two events allow the user to detect this mechanism:\n\n- ``EXEC_TRANSFER_CALL``: called before restoring native execution for the method.\n- ``EXEC_TRANSFER_RETURN``: called after the execution of the method.\n\nThese two events can be used to retrieve the method and its parameters before the call, and its return value afterward.\n``EXEC_TRANSFER_CALL`` can also be used to emulate a method call.\n\n\nGet native call symbols\n-----------------------\n\nWhen QBDI needs to restore the native execution, the user may retrieve the name of the calling method\nbased on the current address. The associated symbol can be found with ``dladdr`` (on Linux and macOS) or ``SymFromAddr`` (on Windows).\n\nWe recommend forcing the linker to resolve all symbols before running the VM. This can be achieved with:\n\n- ``LD_BIND_NOW=1`` on Linux\n- ``DYLD_BIND_AT_LAUNCH=1`` on macOS\n\n\nWith dladdr\n+++++++++++\n\n``dladdr`` may not find the symbol associated with an address if it's not an exported symbol.\nIf several symbols are associated, only one is returned.\n\n.. code:: c\n\n    static VMAction transfertcbk(VMInstanceRef vm, const VMState *vmState, GPRState *gprState, FPRState *fprState, void *data) {\n        Dl_info info = {0};\n        dladdr((void*)gprState->rip, &info);\n\n        if (info.dli_sname != NULL) {\n            printf(\"Call %s (addr: 0x%\" PRIRWORD \")\\n\", info.dli_sname, gprState->rip);\n        } else {\n            printf(\"Call addr: 0x%\" PRIRWORD \"\\n\", gprState->rip);\n        }\n        return QBDI_CONTINUE;\n    }\n\n    qbdi_addVMEventCB(vm, QBDI_EXEC_TRANSFER_CALL, transfertcbk, NULL);\n\n.. code:: python\n\n    import ctypes\n    import ctypes.util\n\n    class Dl_info(ctypes.Structure):\n        _fields_ = [('dli_fname', ctypes.c_char_p),\n                    ('dli_fbase', ctypes.c_void_p),\n                    ('dli_sname', ctypes.c_char_p),\n                    ('dli_saddr', ctypes.c_void_p)]\n\n    libdl_path = ctypes.util.find_library('dl')\n    assert libdl_path is not None\n    libdl = ctypes.cdll.LoadLibrary(libdl_path)\n    libdl.dladdr.argtypes = (ctypes.c_void_p, ctypes.POINTER(Dl_info))\n\n    def dladdr(addr):\n\n        res = Dl_info()\n        result = libdl.dladdr(ctypes.cast(addr, ctypes.c_void_p), ctypes.byref(res))\n\n        return res.dli_sname\n\n    def transfertcbk(vm, vmState, gpr, fpr, data):\n\n        print(\"Call {} (addr: 0x{:x})\".format(\n            dladdr(gpr.rip),\n            gpr.rip))\n\n        return pyqbdi.CONTINUE\n\n    vm.addVMEventCB(pyqbdi.EXEC_TRANSFER_CALL, transfertcbk, None)\n\nWith lief\n+++++++++\n\n`Lief <https://lief.quarkslab.com/>`_ is a C, C++ and Python library\nthat aims to parse ELF, PE and MachO file formats. This library can\nextract all the symbols associated with an address, including the non-exported one.\nThis solution can resolve more addresses, but could be slower than ``dladdr``.\n\nFor an ELF binary, the following code prints for each ``EXEC_TRANSFER_CALL``\nevent, the symbols associated with the target address. For a PE library, the user may\nneed to parse the PDB file of the library to get the symbol associated with the\ntarget address.\n\n.. code:: cpp\n\n    #include <LIEF/LIEF.hpp>\n\n    class Module {\n        public:\n            std::string path;\n            QBDI::Range<QBDI::rword> range;\n\n            Module(const QBDI::MemoryMap& m) : path(m.name), range(m.range) {}\n\n            void append(const QBDI::MemoryMap& m) {\n                if (m.range.start() < range.start()) {\n                    range.setStart(m.range.start());\n                }\n                if (m.range.end() > range.end()) {\n                    range.setEnd(m.range.end());\n                }\n            }\n    };\n\n    class AddrResolver {\n        private:\n            std::vector<Module> modules;\n            std::unordered_set<std::string> loaded_path;\n            std::unordered_map<QBDI::rword, std::unordered_set<std::string>> resolv_cache;\n\n            void cacheModules();\n            const Module* getModule(QBDI::rword addr, bool reload = true);\n            void loadModule(const Module& m);\n\n        public:\n            AddrResolver() {\n                cacheModules();\n            }\n\n            const std::unordered_set<std::string>& resolve(QBDI::rword addr);\n    };\n\n    void AddrResolver::cacheModules() {\n        modules.clear();\n\n        for (const auto& map : QBDI::getCurrentProcessMaps(true)) {\n            auto r = std::find_if(std::begin(modules), std::end(modules),\n                    [&](const Module& m){return m.path == map.name;});\n            if (r != std::end(modules)) {\n                r->append(map);\n            } else if (map.name.find(\"/\") != std::string::npos) {\n                modules.emplace_back(map);\n            }\n        }\n    }\n\n    const Module* AddrResolver::getModule(QBDI::rword addr, bool reload) {\n        const auto r = std::find_if(std::begin(modules), std::end(modules),\n                [&](const Module& m){return m.range.contains(addr);});\n        if (r != std::end(modules)) {\n            return &*r;\n        } else if (reload) {\n            cacheModules();\n            return getModule(addr, false);\n        } else {\n            return nullptr;\n        }\n    }\n\n    void AddrResolver::loadModule(const Module& m) {\n        std::cout << \"Load Module \" << m.path << std::endl;\n        if (loaded_path.find(m.path) != loaded_path.end()) {\n            return;\n        }\n        std::unique_ptr<LIEF::ELF::Binary> externlib = LIEF::ELF::Parser::parse(m.path);\n        if (not externlib) {\n            return;\n        }\n        for (const auto& s: externlib->symbols()) {\n            QBDI::rword addr = s.value() + m.range.start();\n            resolv_cache[addr].emplace(s.demangled_name());\n        }\n\n        loaded_path.emplace(m.path);\n    }\n\n    const std::unordered_set<std::string>& AddrResolver::resolve(QBDI::rword addr) {\n        const auto & symnames = resolv_cache[addr];\n        if (!symnames.empty()) {\n            return symnames;\n        }\n        std::cout << std::setbase(16) << \"Failed to find 0x\" << addr << std::endl;\n        const Module* m = getModule(addr);\n        if (m != nullptr) {\n            loadModule(*m);\n        }\n        return symnames;\n    }\n\n    QBDI::VMAction transfertCBK(QBDI::VMInstanceRef vm, const QBDI::VMState* vmState, QBDI::GPRState* gprState, QBDI::FPRState* fprState, void* data) {\n        const std::unordered_set<std::string>& r = static_cast<AddrResolver*>(data)->resolve(gprState->rip);\n\n        if (r.empty()) {\n            std::cout << std::setbase(16) << \"Call addr: 0x\" << gprState->rip << std::endl;\n        } else {\n            std::cout << \"Call \";\n            for (const auto& s: r) {\n                std::cout << s << \" \";\n            }\n            std::cout << std::setbase(16) << \"(addr: 0x\" << gprState->rip << \")\" << std::endl;\n        }\n        return QBDI::CONTINUE;\n    }\n\n    AddrResolver data;\n    vm->addVMEventCB(QBDI::EXEC_TRANSFER_CALL, transfertCBK, &data);\n\n\n\n.. code:: python\n\n    import lief\n    import pyqbdi\n\n    class Module:\n        def __init__(self, module):\n            self.name = module.name\n            self.range = pyqbdi.Range(module.range.start, module.range.end)\n\n        def append(self, module):\n            assert module.name == self.name\n            if module.range.start < self.range.start:\n                self.range.start = module.range.start\n            if self.range.end < module.range.end:\n                self.range.end = module.range.end\n\n    class AddrResolver:\n\n        def __init__(self):\n            self.lib_cache = []\n            self.resolv_cache = {}\n            self.map_cache = self.get_exec_maps()\n\n        def get_exec_maps(self):\n            maps = {}\n            for m in pyqbdi.getCurrentProcessMaps(True):\n                if m.name in maps:\n                    maps[m.name].append(m)\n                elif '/' in m.name:\n                    maps[m.name] = Module(m)\n            return maps\n\n        def get_addr_maps(self, addr):\n            for _, m in self.map_cache.items():\n                if addr in m.range:\n                    return m\n            self.map_cache = self.get_exec_maps()\n            for _, m in self.map_cache.items():\n                if addr in m.range:\n                    return m\n            return None\n\n        def load_lib(self, maps):\n            if maps.name in self.lib_cache:\n                return\n\n            # use lief.PE or lief.MACO if not ELF file\n            lib = lief.ELF.parse(maps.name)\n            if lib is None:\n                return\n\n            for s in lib.symbols:\n                addr = s.value + maps.range.start\n                if addr in self.resolv_cache:\n                    if s.name not in self.resolv_cache[addr]:\n                        self.resolv_cache[addr].append(s.name)\n                else:\n                    self.resolv_cache[addr] = [s.name]\n\n            self.lib_cache.append(maps.name)\n\n        def get_names(self, addr):\n\n            if addr in self.resolv_cache:\n                return self.resolv_cache[addr]\n\n            maps = self.get_addr_maps(addr)\n            if maps is None:\n                return []\n            self.load_lib(maps)\n            if addr in self.resolv_cache:\n                return self.resolv_cache[addr]\n            self.resolv_cache[addr] = []\n            return []\n\n    def transfertcbk(vm, vmState, gpr, fpr, data):\n\n        f_names = data['resolver'].get_names(gpr.rip)\n        if f_names != []:\n            print(\"Call {} (addr: 0x{:x})\".format(f_names, gpr.rip))\n        else:\n            print(\"Call addr: 0x{:x}\".format(gpr.rip))\n\n        return pyqbdi.CONTINUE\n\n    ctx = {\n        \"resolver\": AddrResolver(),\n    }\n\n    vm.addVMEventCB(pyqbdi.EXEC_TRANSFER_CALL, transfertcbk, ctx)\n\nUsing this snippet with PyQBDIPreload prints the libc calls.\n\n.. code:: text\n\n    $ python -m pyqbdipreload test.py ls\n    Call ['__strrchr_avx2'] (addr: 0x7f2aed2a8330)\n    Call ['setlocale', '__GI_setlocale'] (addr: 0x7f2aed17a7f0)\n    Call ['bindtextdomain', '__bindtextdomain'] (addr: 0x7f2aed17e000)\n    Call ['textdomain', '__textdomain'] (addr: 0x7f2aed1815f0)\n    Call ['__cxa_atexit', '__GI___cxa_atexit'] (addr: 0x7f2aed1879b0)\n    Call ['getopt_long'] (addr: 0x7f2aed22d3f0)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    Call ['isatty', '__isatty'] (addr: 0x7f2aed239250)\n    Call ['ioctl', '__ioctl', '__GI_ioctl', '__GI___ioctl'] (addr: 0x7f2aed23d590)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    Call ['__errno_location', '__GI___errno_location'] (addr: 0x7f2aed16fde0)\n    Call ['__libc_malloc', 'malloc', '__GI___libc_malloc', '__malloc'] (addr: 0x7f2aed1d3320)\n    Call ['__memcpy_avx_unaligned', '__memmove_avx_unaligned'] (addr: 0x7f2aed2ab4a0)\n    Call ['__errno_location', '__GI___errno_location'] (addr: 0x7f2aed16fde0)\n    Call ['__libc_malloc', 'malloc', '__GI___libc_malloc', '__malloc'] (addr: 0x7f2aed1d3320)\n    Call ['__memcpy_avx_unaligned', '__memmove_avx_unaligned'] (addr: 0x7f2aed2ab4a0)\n    Call ['getenv', '__GI_getenv'] (addr: 0x7f2aed186b20)\n    ....\n"
  },
  {
    "path": "examples/CMakeLists.txt",
    "content": "add_executable(mnemonicFilter mnemonicFilter.cpp)\ntarget_link_libraries(mnemonicFilter QBDI)\nset_target_properties(mnemonicFilter PROPERTIES CXX_STANDARD 11\n                                                CXX_STANDARD_REQUIRED ON)\nadd_signature(mnemonicFilter)\n\nadd_executable(modules_c modules.c)\ntarget_link_libraries(modules_c QBDI)\nadd_signature(modules_c)\n\nadd_executable(modules_cpp modules.cpp)\ntarget_link_libraries(modules_cpp QBDI)\nset_target_properties(modules_cpp PROPERTIES CXX_STANDARD 11\n                                             CXX_STANDARD_REQUIRED ON)\nadd_signature(modules_cpp)\n\nadd_executable(thedude_c thedude.c)\ntarget_link_libraries(thedude_c QBDI)\nadd_signature(thedude_c)\n\nadd_executable(thedude_cpp thedude.cpp)\ntarget_link_libraries(thedude_cpp QBDI)\nset_target_properties(thedude_cpp PROPERTIES CXX_STANDARD 11\n                                             CXX_STANDARD_REQUIRED ON)\nadd_signature(thedude_cpp)\n\nadd_executable(cryptolock_cpp cryptolock.cpp)\ntarget_link_libraries(cryptolock_cpp QBDI)\nset_target_properties(cryptolock_cpp PROPERTIES CXX_STANDARD 11\n                                                CXX_STANDARD_REQUIRED ON)\nadd_signature(cryptolock_cpp)\n\nadd_executable(cryptolock_c cryptolock.c)\ntarget_link_libraries(cryptolock_c QBDI)\nadd_signature(cryptolock_c)\n\nadd_subdirectory(c)\nadd_subdirectory(cpp)\n"
  },
  {
    "path": "examples/Dockerfile",
    "content": "FROM qbdi/qbdi:x86_ubuntu\n\nENV USER=\"docker\" \\\n    HOME=\"/home/docker\"\n\n# install some needed tools\nRUN apt-get update && \\\n    apt-get upgrade -y && \\\n    apt-get install -y \\\n        adduser \\\n        build-essential \\\n        cmake \\\n        libstdc++-13-dev \\\n        python \\\n        python-dev \\\n        #gdb \\\n        #vim \\\n        sudo \\\n        bash && \\\n    apt-get clean && \\\n    rm -rf /var/lib/apt/lists/*\n\n# create a user\nRUN adduser --disabled-password --gecos '' $USER && \\\n    adduser $USER sudo && \\\n    echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers\n\n# switch to new user\nUSER $USER\nWORKDIR $HOME\n\n# TODO : Add yours needed files\n#ADD --chown=$USER . $HOME/\n\nCMD [\"/bin/bash\"]\n\n"
  },
  {
    "path": "examples/c/CMakeLists.txt",
    "content": "add_executable(fibonacci_c fibonacci.c)\ntarget_link_libraries(fibonacci_c QBDI)\nadd_signature(fibonacci_c)\n\nif(QBDI_TOOLS_QBDIPRELOAD)\n  add_library(tracer_preload_c SHARED tracer_preload.c)\n  target_link_libraries(tracer_preload_c QBDIPreload QBDI_static)\nendif()\n"
  },
  {
    "path": "examples/c/fibonacci.c",
    "content": "#include <assert.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"QBDI.h\"\n\nint fibonacci(int n) {\n  if (n <= 2)\n    return 1;\n  return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nVMAction showInstruction(VMInstanceRef vm, GPRState *gprState,\n                         FPRState *fprState, void *data) {\n  // Obtain an analysis of the instruction from the VM\n  const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(\n      vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);\n\n  // Printing disassembly\n  printf(\"0x%\" PRIRWORD \": %s\\n\", instAnalysis->address,\n         instAnalysis->disassembly);\n\n  return QBDI_CONTINUE;\n}\n\nVMAction countIteration(VMInstanceRef vm, GPRState *gprState,\n                        FPRState *fprState, void *data) {\n  (*((int *)data))++;\n\n  return QBDI_CONTINUE;\n}\n\nstatic const uint32_t STACK_SIZE = 0x100000; // 1MB\n\nint main(int argc, char **argv) {\n  int n = 0;\n\n  int iterationCount = 0;\n  uint8_t *fakestack;\n  VMInstanceRef vm;\n  GPRState *state;\n  rword retvalue;\n\n  if (argc >= 2) {\n    n = atoi(argv[1]);\n  }\n  if (n < 1) {\n    n = 1;\n  }\n\n  // Constructing a new QBDI VM\n  qbdi_initVM(&vm, NULL, NULL, 0);\n\n  // Get a pointer to the GPR state of the VM\n  state = qbdi_getGPRState(vm);\n  assert(state != NULL);\n\n  // Setup initial GPR state,\n  qbdi_allocateVirtualStack(state, STACK_SIZE, &fakestack);\n\n  // Registering showInstruction() callback to print a trace of the execution\n  uint32_t cid = qbdi_addCodeCB(vm, QBDI_PREINST, showInstruction, NULL, 0);\n  assert(cid != QBDI_INVALID_EVENTID);\n\n  // Registering countIteration() callback\n  qbdi_addMnemonicCB(vm, \"CALL*\", QBDI_PREINST, countIteration, &iterationCount,\n                     0);\n\n  // Setup Instrumentation Range\n  bool res = qbdi_addInstrumentedModuleFromAddr(vm, (rword)&fibonacci);\n  assert(res == true);\n\n  // Running DBI execution\n  printf(\"Running fibonacci(%d) ...\\n\", n);\n  res = qbdi_call(vm, &retvalue, (rword)fibonacci, 1, n);\n  assert(res == true);\n\n  printf(\"fibonnaci(%d) returns %zd after %d recursions\\n\", n, retvalue,\n         iterationCount);\n\n  // cleanup\n  qbdi_alignedFree(fakestack);\n  qbdi_terminateVM(vm);\n\n  return 0;\n}\n"
  },
  {
    "path": "examples/c/tracer_preload.c",
    "content": "#include <stdio.h>\n#include \"QBDIPreload.h\"\n\nQBDIPRELOAD_INIT;\n\nstatic VMAction onInstruction(VMInstanceRef vm, GPRState *gprState,\n                              FPRState *fprState, void *data) {\n  const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(\n      vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);\n  printf(\"0x%\" PRIRWORD \" %s\\n\", instAnalysis->address,\n         instAnalysis->disassembly);\n  return QBDI_CONTINUE;\n}\n\nint qbdipreload_on_start(void *main) {\n#if defined(_WIN32) || defined(WIN32) // Allow logging to show for GUI targets\n  FILE *oldStream;\n  AllocConsole();\n  freopen_s(&oldStream, \"CONOUT$\", \"w\", stdout);\n#endif\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_premain(void *gprCtx, void *fpuCtx) {\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_main(int argc, char **argv) {\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_run(VMInstanceRef vm, rword start, rword stop) {\n  qbdi_addCodeCB(vm, QBDI_PREINST, onInstruction, NULL, 0);\n  qbdi_run(vm, start, stop);\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint qbdipreload_on_exit(int status) { return QBDIPRELOAD_NO_ERROR; }\n"
  },
  {
    "path": "examples/cpp/CMakeLists.txt",
    "content": "add_executable(fibonacci_cpp fibonacci.cpp)\ntarget_link_libraries(fibonacci_cpp QBDI)\nset_target_properties(fibonacci_cpp PROPERTIES CXX_STANDARD 11\n                                               CXX_STANDARD_REQUIRED ON)\nadd_signature(fibonacci_cpp)\n\nif(QBDI_TOOLS_QBDIPRELOAD)\n  add_library(tracer_preload_cpp SHARED tracer_preload.cpp)\n  target_link_libraries(tracer_preload_cpp QBDIPreload QBDI_static)\n  set_target_properties(tracer_preload_cpp PROPERTIES CXX_STANDARD 11\n                                                      CXX_STANDARD_REQUIRED ON)\nendif()\n"
  },
  {
    "path": "examples/cpp/fibonacci.cpp",
    "content": "#include <assert.h>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <iomanip>\n#include <iostream>\n\n#include \"QBDI.h\"\n\nint fibonacci(int n) {\n  if (n <= 2)\n    return 1;\n  return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\nQBDI::VMAction showInstruction(QBDI::VM *vm, QBDI::GPRState *gprState,\n                               QBDI::FPRState *fprState, void *data) {\n  // Obtain an analysis of the instruction from the VM\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n\n  // Printing disassembly\n  std::cout << std::setbase(16) << instAnalysis->address << \": \"\n            << instAnalysis->disassembly << std::endl\n            << std::setbase(10);\n\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction countIteration(QBDI::VM *vm, QBDI::GPRState *gprState,\n                              QBDI::FPRState *fprState, void *data) {\n  (*((unsigned *)data))++;\n\n  return QBDI::CONTINUE;\n}\n\nstatic const size_t STACK_SIZE = 0x100000; // 1MB\n\nint main(int argc, char **argv) {\n  int n = 0;\n  bool res;\n  uint32_t cid;\n\n  unsigned iterationCount = 0;\n  uint8_t *fakestack;\n  QBDI::GPRState *state;\n  QBDI::rword retvalue;\n\n  // Argument to the fibonnaci call\n  if (argc >= 2) {\n    n = atoi(argv[1]);\n  }\n  if (n < 1) {\n    n = 1;\n  }\n\n  // Constructing a new QBDI vm\n  QBDI::VM vm{};\n\n  // Get a pointer to the GPR state of the VM\n  state = vm.getGPRState();\n  assert(state != nullptr);\n\n  // Setup initial GPR state,\n  res = QBDI::allocateVirtualStack(state, STACK_SIZE, &fakestack);\n  assert(res == true);\n\n  // Registering showInstruction() callback to print a trace of the execution\n  cid = vm.addCodeCB(QBDI::PREINST, showInstruction, nullptr);\n  assert(cid != QBDI::INVALID_EVENTID);\n\n  // Registering countIteration() callback\n  vm.addMnemonicCB(\"CALL*\", QBDI::PREINST, countIteration, &iterationCount);\n\n  // Setup Instrumentation Range\n  res = vm.addInstrumentedModuleFromAddr(\n      reinterpret_cast<QBDI::rword>(fibonacci));\n  assert(res == true);\n\n  // Running DBI execution\n  std::cout << \"Running fibonacci(\" << n << \") ...\" << std::endl;\n  res = vm.call(&retvalue, reinterpret_cast<QBDI::rword>(fibonacci),\n                {static_cast<QBDI::rword>(n)});\n  assert(res == true);\n\n  std::cout << \"fibonnaci(\" << n << \") returns \" << retvalue << \" after \"\n            << iterationCount << \" recursions\" << std::endl;\n\n  QBDI::alignedFree(fakestack);\n\n  return 0;\n}\n"
  },
  {
    "path": "examples/cpp/tracer_preload.cpp",
    "content": "#include <iomanip>\n#include <iostream>\n\n#include \"QBDIPreload.h\"\n\nstatic QBDI::VMAction onInstruction(QBDI::VMInstanceRef vm,\n                                    QBDI::GPRState *gprState,\n                                    QBDI::FPRState *fprState, void *data) {\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n\n  std::cout << std::setbase(16) << instAnalysis->address << \": \"\n            << instAnalysis->disassembly << std::endl\n            << std::setbase(10);\n  return QBDI::CONTINUE;\n}\n\nextern \"C\" {\n\nQBDIPRELOAD_INIT;\n\nint qbdipreload_on_start(void *main) {\n#if defined(_WIN32) || defined(WIN32) // Allow logging to show for GUI targets\n  FILE *oldStream;\n  AllocConsole();\n  freopen_s(&oldStream, \"CONOUT$\", \"w\", stdout);\n#endif\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_premain(void *gprCtx, void *fpuCtx) {\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_main(int argc, char **argv) {\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_run(QBDI::VMInstanceRef vm, QBDI::rword start,\n                       QBDI::rword stop) {\n  vm->addCodeCB(QBDI::PREINST, onInstruction, nullptr);\n  vm->run(start, stop);\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint qbdipreload_on_exit(int status) { return QBDIPRELOAD_NO_ERROR; }\n}\n"
  },
  {
    "path": "examples/cryptolock.c",
    "content": "#include <inttypes.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"QBDI.h\"\n\nQBDI_NOINLINE void hashPassword(char *hash, const char *password) {\n  char acc = 42;\n  size_t i = 0;\n  size_t hash_len = strlen(hash);\n  size_t password_len = strlen(password);\n\n  for (i = 0; i < hash_len && i < password_len; i++) {\n    hash[i] = (hash[i] ^ acc) - password[i];\n    acc = password[i];\n  }\n}\n\nchar SECRET[] =\n    \"\\x29\\x0d\\x20\\x00\\x00\\x00\\x00\\x0a\\x65\\x1f\\x32\\x00\\x19\\x0c\\x4e\"\n    \"\\x1b\\x2d\\x09\\x66\\x0c\\x1a\\x06\\x05\\x06\\x20\\x1f\\x46\";\n\nQBDI_NOINLINE const char *getSecret(const char *password) {\n  size_t i = 0;\n  size_t password_len = strlen(password);\n  for (i = 0; i < sizeof(SECRET); i++) {\n    SECRET[i] ^= password[i % password_len];\n  }\n  return SECRET;\n}\n\nQBDI_NOINLINE const char *cryptolock(const char *password) {\n  char hash[] = \"\\x6f\\x29\\x2a\\x29\\x1a\\x1c\\x07\\x01\";\n  size_t i = 0;\n\n  hashPassword(hash, password);\n\n  bool good = true;\n  for (i = 0; i < sizeof(hash); i++) {\n    if (hash[i] != 0) {\n      good = false;\n    }\n  }\n\n  if (good) {\n    return getSecret(password);\n  } else {\n    return NULL;\n  }\n}\n\nVMAction onwrite(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n                 void *data) {\n  size_t i = 0;\n  size_t num_access = 0;\n  // Obtain an analysis of the instruction from the vm\n  const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(\n      vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);\n  // Obtain the instruction memory accesses\n  MemoryAccess *memAccesses = qbdi_getInstMemoryAccess(vm, &num_access);\n\n  // Printing disassembly\n  printf(\"%\" PRIRWORD \": %s\\n\", instAnalysis->address,\n         instAnalysis->disassembly);\n  // Printing write memory accesses\n  for (i = 0; i < num_access; i++) {\n    // Checking the access mode\n    if (memAccesses[i].type == QBDI_MEMORY_WRITE) {\n      // Writing the written value, the size and the address\n      printf(\"\\tWrote 0x%\" PRIRWORD \" on %u bytes at 0x%\" PRIRWORD \"\\n\",\n             memAccesses[i].value, (unsigned)memAccesses[i].size,\n             memAccesses[i].accessAddress);\n    }\n  }\n  printf(\"\\n\");\n  free(memAccesses);\n\n  return QBDI_CONTINUE;\n}\n\nstatic const uint32_t STACK_SIZE = 0x100000; // 1MB\nstatic const rword FAKE_RET_ADDR = 0x40000;\n\nint main(int argc, char **argv) {\n  uint8_t *fakestack = NULL;\n  VMInstanceRef vm;\n  GPRState *state;\n\n  if (argc < 2) {\n    printf(\"Please give a password as first argument\\n\");\n    return 1;\n  }\n\n  printf(\"Initializing VM ...\\n\");\n  // Constructing a new QBDI vm\n  qbdi_initVM(&vm, NULL, NULL, 0);\n  // Registering a callback on every memory write to our onwrite() function\n  qbdi_addMemAccessCB(vm, QBDI_MEMORY_WRITE, onwrite, NULL, 0);\n  // Instrument this module\n  qbdi_addInstrumentedModuleFromAddr(vm, (rword)&cryptolock);\n  // Get a pointer to the GPR state of the vm\n  state = qbdi_getGPRState(vm);\n  // Setup initial GPR state, this fakestack will produce a ret FAKE_RET_ADDR\n  //   at the end of the execution\n  // Also setup one argument on the stack which is the password string\n  qbdi_allocateVirtualStack(state, STACK_SIZE, &fakestack);\n  qbdi_simulateCall(state, FAKE_RET_ADDR, 1, argv[1]);\n\n  printf(\"Running cryptolock(\\\"%s\\\")\\n\", argv[1]);\n  qbdi_run(vm, (rword)cryptolock, (rword)FAKE_RET_ADDR);\n  // Getting the return value from the call\n  const char *ret = (const char *)QBDI_GPR_GET(state, REG_RETURN);\n  // If it is not null, display it\n  if (ret != NULL) {\n    printf(\"Returned \\\"%s\\\"\\n\", ret);\n  } else {\n    printf(\"Returned null\\n\");\n  }\n\n  qbdi_terminateVM(vm);\n  qbdi_alignedFree(fakestack);\n\n  return 0;\n}\n"
  },
  {
    "path": "examples/cryptolock.cpp",
    "content": "#include <cstring>\n#include <iomanip>\n#include <iostream>\n#include <vector>\n\n#include \"QBDI.h\"\n\nQBDI_NOINLINE void hashPassword(char *hash, const char *password) {\n  char acc = 42;\n  size_t hash_len = strlen(hash);\n  size_t password_len = strlen(password);\n\n  for (size_t i = 0; i < hash_len && i < password_len; i++) {\n    hash[i] = (hash[i] ^ acc) - password[i];\n    acc = password[i];\n  }\n}\n\nchar SECRET[] =\n    \"\\x29\\x0d\\x20\\x00\\x00\\x00\\x00\\x0a\\x65\\x1f\\x32\\x00\\x19\\x0c\\x4e\"\n    \"\\x1b\\x2d\\x09\\x66\\x0c\\x1a\\x06\\x05\\x06\\x20\\x1f\\x46\";\n\nQBDI_NOINLINE const char *getSecret(const char *password) {\n  size_t password_len = strlen(password);\n  for (size_t i = 0; i < sizeof(SECRET); i++) {\n    SECRET[i] ^= password[i % password_len];\n  }\n  return SECRET;\n}\n\nQBDI_NOINLINE const char *cryptolock(const char *password) {\n  char hash[] = \"\\x6f\\x29\\x2a\\x29\\x1a\\x1c\\x07\\x01\";\n\n  hashPassword(hash, password);\n\n  bool good = true;\n  for (size_t i = 0; i < sizeof(hash); i++) {\n    if (hash[i] != 0) {\n      good = false;\n    }\n  }\n\n  if (good) {\n    return getSecret(password);\n  } else {\n    return nullptr;\n  }\n}\n\nQBDI::VMAction onwrite(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                       QBDI::FPRState *fprState, void *data) {\n  // Obtain an analysis of the instruction from the vm\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  // Obtain the instruction memory accesses\n  std::vector<QBDI::MemoryAccess> memAccesses = vm->getInstMemoryAccess();\n\n  // Printing disassembly\n  std::cout << std::setbase(16) << instAnalysis->address << \": \"\n            << instAnalysis->disassembly << std::endl\n            << std::setbase(10);\n  // Printing write memory accesses\n  for (const QBDI::MemoryAccess &memAccess : memAccesses) {\n    // Checking the access mode\n    if (memAccess.type == QBDI::MEMORY_WRITE) {\n      // Writing the written value, the size and the address\n      std::cout << \"\\t\"\n                << \"Wrote 0x\" << std::setbase(16) << memAccess.value << \" on \"\n                << std::setbase(10) << (int)memAccess.size << \" bytes at 0x\"\n                << std::setbase(16) << memAccess.accessAddress << std::endl;\n    }\n  }\n  std::cout << std::endl;\n\n  return QBDI::CONTINUE;\n}\n\nstatic const size_t STACK_SIZE = 0x100000; // 1MB\nstatic const QBDI::rword FAKE_RET_ADDR = 0x40000;\n\nint main(int argc, char **argv) {\n  uint8_t *fakestack = nullptr;\n  QBDI::GPRState *state;\n\n  if (argc < 2) {\n    std::cout << \"Please give a password as first argument\" << std::endl;\n    return 1;\n  }\n\n  std::cout << \"Initializing VM ...\" << std::endl;\n  // Constructing a new QBDI vm\n  QBDI::VM *vm = new QBDI::VM();\n  // Registering a callback on every memory write to our onwrite() function\n  vm->addMemAccessCB(QBDI::MEMORY_WRITE, onwrite, nullptr);\n  // Instrument this module\n  vm->addInstrumentedModuleFromAddr((QBDI::rword)&cryptolock);\n  // Get a pointer to the GPR state of the vm\n  state = vm->getGPRState();\n  // Setup initial GPR state, this fakestack will produce a ret FAKE_RET_ADDR\n  //   at the end of the execution\n  // Also setup one argument on the stack which is the password string\n  QBDI::allocateVirtualStack(state, STACK_SIZE, &fakestack);\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {(QBDI::rword)argv[1]});\n\n  std::cout << \"Running cryptolock(\\\"\" << argv[1] << \"\\\")\" << std::endl;\n  vm->run((QBDI::rword)cryptolock, (QBDI::rword)FAKE_RET_ADDR);\n  // Getting the return value from the call\n  const char *ret = (const char *)QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  // If it is not null, display it\n  if (ret != nullptr) {\n    std::cout << \"Returned \\\"\" << ret << \"\\\"\" << std::endl;\n  } else {\n    std::cout << \"Returned null\" << std::endl;\n  }\n\n  delete vm;\n  QBDI::alignedFree(fakestack);\n\n  return 0;\n}\n"
  },
  {
    "path": "examples/mnemonicFilter.cpp",
    "content": "#include <inttypes.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"QBDI.h\"\n\n#define RED \"\\x1b[31m\"\n#define MAGENTA \"\\x1b[35m\"\n#define RESET \"\\x1b[0m\"\n\nint HW(void) {\n  int i;\n  for (i = 0; i < 10; i++) {\n    printf(\"Hello World !\\n\");\n  }\n  return 0;\n}\n\nQBDI::VMAction CBMnemonic(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                          QBDI::FPRState *fprState, void *data) {\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  printf(\"MNEMONIC @ %s%p%s =>%s%s%s\\n\", RED, (void *)instAnalysis->address,\n         RESET, MAGENTA, instAnalysis->disassembly, RESET);\n  return QBDI::VMAction::CONTINUE;\n}\n\nint main(int argc, char **argv) {\n  uint8_t *fakestack = nullptr;\n  QBDI::GPRState *state;\n\n  QBDI::setLogPriority(QBDI::LogPriority::DEBUG);\n\n  // Constructing a new QBDI vm\n  QBDI::VM *vm = new QBDI::VM();\n\n  // Add a callback on every instruction\n  // with a mnemonic starting with 'j' (ex: jmp)\n  vm->addMnemonicCB(\"j*\", QBDI::POSTINST, CBMnemonic, NULL);\n\n  // Get a pointer to the GPR state of the vm\n  state = vm->getGPRState();\n  // Setup initial GPR state, this fakestack will produce\n  // a ret NULL at the end of the execution\n  QBDI::allocateVirtualStack(state, 0x100000, &fakestack);\n  // Add our binary to the instrumented range\n  vm->addInstrumentedModuleFromAddr((QBDI::rword)&HW);\n  // Call the hello world function\n  QBDI::simulateCall(state, 0x0);\n  vm->run((QBDI::rword)HW, (QBDI::rword)0x0);\n\n  delete vm;\n  QBDI::alignedFree(fakestack);\n  return 0;\n}\n"
  },
  {
    "path": "examples/modules.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\n#include <QBDI.h>\n\nint main(int argc, char **argv) {\n  size_t size = 0, i = 0;\n  char **modules = qbdi_getModuleNames(&size);\n\n  for (i = 0; i < size; i++) {\n    printf(\"%s\\n\", modules[i]);\n  }\n\n  for (i = 0; i < size; i++) {\n    free(modules[i]);\n  }\n  free(modules);\n\n  qbdi_MemoryMap *maps = qbdi_getCurrentProcessMaps(false, &size);\n  for (i = 0; i < size; i++) {\n    printf(\"%s (%c%c%c) \", maps[i].name,\n           maps[i].permission & QBDI_PF_READ ? 'r' : '-',\n           maps[i].permission & QBDI_PF_WRITE ? 'w' : '-',\n           maps[i].permission & QBDI_PF_EXEC ? 'x' : '-');\n    printf(\"(%#\" PRIRWORD \", %#\" PRIRWORD \")\\n\", maps[i].start, maps[i].end);\n  }\n  qbdi_freeMemoryMapArray(maps, size);\n\n  return 0;\n}\n"
  },
  {
    "path": "examples/modules.cpp",
    "content": "#include <iostream>\n\n#include <QBDI.h>\n\nint main(int argc, char **argv) {\n  for (const QBDI::MemoryMap &m : QBDI::getCurrentProcessMaps()) {\n    std::cout << m.name << \" (\" << m.permission << \") \";\n    m.range.display(std::cout);\n    std::cout << std::endl;\n  }\n}\n"
  },
  {
    "path": "examples/pyqbdi/coverage.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport pyqbdi\nimport struct\nimport atexit\n\nclass CovModule:\n    # Create the full range memory for all module\n\n    def __init__(self, module):\n        self.name = module.name\n        self.range = pyqbdi.Range(module.range.start, module.range.end)\n        self.executable = module.permission & pyqbdi.PF_EXEC\n\n    def append(self, module):\n        assert module.name == self.name\n        if module.range.start < self.range.start:\n            self.range.start = module.range.start\n        if self.range.end < module.range.end:\n            self.range.end = module.range.end\n        self.executable |= module.permission & pyqbdi.PF_EXEC\n\ndef get_modules():\n    _modules = {}\n    for m in pyqbdi.getCurrentProcessMaps(True):\n        if m.name in _modules:\n            _modules[m.name].append(m)\n        elif '/' in m.name:\n            _modules[m.name] = CovModule(m)\n\n    _modules = {k: v for k, v in _modules.items() if v.executable}\n\n    modules = []\n    for (_, m) in _modules.items():\n        for n in modules:\n            assert not m.range.overlaps(n.range), \"Error when try to identify module range\"\n        modules.append(m)\n    return modules\n\n\n# write coverage (in drcov format)\ndef writeCoverage(stats):\n    filename = \"a.cov\"\n    with open(filename, \"w\") as fp:\n        # write header\n        fp.write(\"DRCOV VERSION: 2\\n\")\n        fp.write(\"DRCOV FLAVOR: drcov\\n\")\n        modules = get_modules()\n        fp.write(\"Module Table: version 2, count %u\\n\" % len(modules))\n        fp.write(\"Columns: id, base, end, entry, path\\n\")\n        # write modules\n        fmt = \"%2u, 0x%x, 0x%x, 0x0000000000000000, %s\\n\"\n        for mid, m in enumerate(modules):\n            fp.write(fmt % (mid, m.range[0], m.range[1], m.name))\n        addrs = stats[\"addrs\"]\n        sizes = stats[\"sizes\"]\n        # find address offsets from their module base\n        starts = {}\n        for addr in addrs:\n            for mid, m in enumerate(modules):\n                if addr >= m.range[0] and addr < m.range[1]:\n                    starts[addr] = (mid, addr - m.range[0])\n                    break\n        # write basicblocks\n        fp.write(\"BB Table: %u bbs\\n\" % len(starts))\n\n    with open(filename, \"ab\") as fp:\n        for addr, (mid, start) in starts.items():\n            # uint32_t start; uint16_t size; uint16_t id;\n            bb = struct.pack(\"<IHH\", start, sizes[addr], mid)\n            fp.write(bb)\n\n\ndef vmCB(vm, evt, gpr, fpr, data):\n    if evt.event & pyqbdi.BASIC_BLOCK_ENTRY:\n        addr = evt.basicBlockStart\n        size = evt.basicBlockEnd - addr\n        data[\"addrs\"].add(addr)\n        data[\"sizes\"][addr] = size\n    return pyqbdi.CONTINUE\n\ndef pyqbdipreload_on_run(vm, start, stop):\n\n    # setup statistics\n    stats = dict(addrs=set(), sizes=dict())\n    # add callback on Basic blocks\n    vm.addVMEventCB(pyqbdi.BASIC_BLOCK_ENTRY, vmCB, stats)\n    # write coverage on exit\n    atexit.register(writeCoverage, stats)\n    # run program\n    vm.run(start, stop)\n\n"
  },
  {
    "path": "examples/pyqbdi/trace_preload.py",
    "content": "#!/usr/bin/env python3\n\nimport pyqbdi\n\ndef showInstruction(vm, gpr, fpr, data):\n    instAnalysis = vm.getInstAnalysis()\n    print(\"0x{:x}: {}\".format(instAnalysis.address, instAnalysis.disassembly))\n    return pyqbdi.CONTINUE\n\n\ndef pyqbdipreload_on_run(vm, start, stop):\n    vm.addCodeCB(pyqbdi.PREINST, showInstruction, None)\n    vm.run(start, stop)\n\n"
  },
  {
    "path": "examples/pyqbdi/trace_sin.py",
    "content": "#!/usr/bin/env python3\n\nimport sys\nimport math\nimport ctypes\nimport pyqbdi\nimport struct\n\ndef insnCB(vm, gpr, fpr, data):\n    instAnalysis = vm.getInstAnalysis()\n    print(\"0x{:x}: {}\".format(instAnalysis.address, instAnalysis.disassembly))\n    return pyqbdi.CONTINUE\n\n\ndef run():\n    # get sin function ptr\n    if sys.platform == 'darwin':\n        libmname = 'libSystem.dylib'\n    elif sys.platform == 'win32':\n        libmname = 'api-ms-win-crt-math-l1-1-0.dll'\n    else:\n        libmname = 'libm.so.6'\n    libm = ctypes.cdll.LoadLibrary(libmname)\n    funcPtr = ctypes.cast(libm.sin, ctypes.c_void_p).value\n\n    # init VM\n    vm = pyqbdi.VM()\n\n    # create stack\n    state = vm.getGPRState()\n    addr = pyqbdi.allocateVirtualStack(state, 0x100000)\n    assert addr is not None\n\n    # instrument library and register memory access\n    vm.addInstrumentedModuleFromAddr(funcPtr)\n    vm.recordMemoryAccess(pyqbdi.MEMORY_READ_WRITE)\n\n    # add callbacks on instructions\n    vm.addCodeCB(pyqbdi.PREINST, insnCB, None)\n\n    # Cast double arg to long and set FPR\n    arg = 1.0\n    carg = struct.pack('<d', arg)\n    fpr = vm.getFPRState()\n    fpr.xmm0 = carg\n\n    # call sin(1.0)\n    pyqbdi.simulateCall(state, 0x42424242)\n    success = vm.run(funcPtr, 0x42424242)\n\n    # Retrieve output FPR state\n    fpr = vm.getFPRState()\n    # Cast long arg to double\n    res = struct.unpack('<d', fpr.xmm0[:8])[0]\n    print(\"%f (python) vs %f (qbdi)\" % (math.sin(arg), res))\n\n    # cleanup\n    pyqbdi.alignedFree(addr)\n\nif __name__ == \"__main__\":\n    run()\n"
  },
  {
    "path": "examples/thedude.c",
    "content": "#include <inttypes.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#include \"QBDI.h\"\n\nvoid stripLF(char *s) {\n  char *pos = strrchr(s, '\\n');\n  if (pos != NULL) {\n    *pos = '\\0';\n  }\n}\n\nQBDI_NOINLINE uint64_t magicPow(uint64_t n, uint64_t e) {\n  uint64_t r = 1;\n  uint64_t i = 0;\n\n  for (i = 0; i < e; i++) {\n    r = (r * n);\n  }\n  return r;\n}\n\nQBDI_NOINLINE uint64_t magicHash(char *secret) {\n  uint64_t hash = 0;\n  uint64_t acc = 1;\n  size_t len = strlen(secret);\n  size_t i = 0;\n\n  for (i = 0; i < len; i++) {\n    uint64_t magic = magicPow(secret[i], acc);\n    hash ^= magic;\n    acc = (acc + magic) % 256;\n  }\n\n  return hash;\n}\n\nQBDI_NOINLINE int thedude() {\n  static int BUFFER_SIZE = 64;\n  time_t t = 0;\n  char *name = (char *)malloc(BUFFER_SIZE);\n  char *secret = (char *)malloc(2 * BUFFER_SIZE);\n  uint64_t hash = 0;\n\n  printf(\"Hi I'm the dude.\\n\");\n  printf(\"Give me your name and I'll give you a hash.\\n\");\n  printf(\"So what's your name ? \");\n  if (!fgets(name, BUFFER_SIZE, stdin)) {\n    return 1;\n  }\n  stripLF(name);\n  time(&t);\n  snprintf(secret, 2 * BUFFER_SIZE, \"%\" PRIu64 \":%s\", (uint64_t)t, name);\n  printf(\"Ok I'll give you the hash of %s.\\n\", secret);\n  hash = magicHash(secret);\n  printf(\"Your hash is %\" PRIx64 \".\\n\", hash);\n  printf(\"No need to thank me.\\n\");\n\n  free(name);\n  free(secret);\n\n  return 0;\n}\n\nVMAction count(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n               void *data) {\n  // Cast data to our CallbackInfo struct\n  uint32_t *counter = (uint32_t *)data;\n  // Obtain an analysis of the instruction from the VM\n  const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(\n      vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);\n  // Printing disassembly\n  printf(\"%\" PRIRWORD \": %s\\n\", instAnalysis->address,\n         instAnalysis->disassembly);\n  // Incrementing the instruction counter\n  (*counter)++;\n  // Signaling the VM to continue execution\n  return QBDI_CONTINUE;\n}\n\nstatic const uint32_t STACK_SIZE = 0x100000; // 1MB\nstatic const rword FAKE_RET_ADDR = 0x40000;\n\nint main(int argc, char **argv) {\n  uint8_t *fakestack;\n  VMInstanceRef vm;\n  GPRState *state;\n  int traceLevel = 0;\n  uint32_t counter = 0;\n  bool instrumented;\n\n  if (argc > 1) {\n    traceLevel = atoi(argv[1]);\n    if (traceLevel > 2) {\n      traceLevel = 2;\n    }\n  }\n\n  printf(\"Initializing VM ...\\n\");\n  // Constructing a new QBDI VM\n  qbdi_initVM(&vm, NULL, NULL, 0);\n  // Registering count() callback to be called after every instruction\n  qbdi_addCodeCB(vm, QBDI_POSTINST, count, &counter, 0);\n\n  // Get a pointer to the GPR state of the VM\n  state = qbdi_getGPRState(vm);\n  // Setup initial GPR state, this fakestack will produce a ret NULL at the end\n  // of the execution\n  qbdi_allocateVirtualStack(state, STACK_SIZE, &fakestack);\n  qbdi_simulateCall(state, FAKE_RET_ADDR, 0);\n\n  printf(\"Running thedude() with trace level %d...\\n\", traceLevel);\n  // Running DBI execution\n  instrumented = qbdi_addInstrumentedModuleFromAddr(vm, (rword)&main);\n  if (instrumented) {\n    if (traceLevel < 1) {\n      qbdi_removeInstrumentedRange(vm, (rword)magicHash, (rword)magicHash + 32);\n    }\n    if (traceLevel < 2) {\n      qbdi_removeInstrumentedRange(vm, (rword)magicPow, (rword)magicPow + 32);\n    }\n    qbdi_run(vm, (rword)thedude, (rword)FAKE_RET_ADDR);\n    printf(\"thedude ran in %u instructions\\n\", counter);\n  } else {\n    printf(\"failed to find main module...\\n\");\n  }\n  qbdi_terminateVM(vm);\n  qbdi_alignedFree(fakestack);\n\n  return 0;\n}\n"
  },
  {
    "path": "examples/thedude.cpp",
    "content": "#include <cinttypes>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <ctime>\n#include <iomanip>\n#include <iostream>\n\n#include \"QBDI.h\"\n\nQBDI_NOINLINE uint64_t magicPow(uint64_t n, uint64_t e) {\n  uint64_t r = 1;\n  uint64_t i = 0;\n\n  for (i = 0; i < e; i++) {\n    r = (r * n);\n  }\n  return r;\n}\n\nQBDI_NOINLINE uint64_t magicHash(const char *secret) {\n  uint64_t hash = 0;\n  uint64_t acc = 1;\n  size_t len = strlen(secret);\n  size_t i = 0;\n\n  for (i = 0; i < len; i++) {\n    uint64_t magic = magicPow(secret[i], acc);\n    hash ^= magic;\n    acc = (acc + magic) % 256;\n  }\n\n  return hash;\n}\n\nQBDI_NOINLINE int thedude() {\n  time_t t = 0;\n  std::string name;\n  uint64_t hash = 0;\n  char *secret = nullptr;\n\n  std::cout << \"Hi I'm the dude.\" << std::endl;\n  std::cout << \"Give me your name and I'll give you a hash.\" << std::endl;\n  std::cout << \"So what's your name ? \";\n  std::cin >> name;\n  time(&t);\n  secret = new char[name.length() + 16 + 2];\n  snprintf(secret, name.length() + 16 + 2, \"%\" PRIu64 \":%s\", (uint64_t)t,\n           name.c_str());\n  std::cout << \"Ok I'll give you the hash of \" << secret << \".\" << std::endl;\n  hash = magicHash(secret);\n  std::cout << \"Your hash is \" << hash << \".\" << std::endl;\n  std::cout << \"No need to thank me.\" << std::endl;\n\n  delete[] secret;\n  return 0;\n}\n\nQBDI::VMAction count(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                     QBDI::FPRState *fprState, void *data) {\n  // Cast data to our counter\n  uint32_t *counter = (uint32_t *)data;\n  // Obtain an analysis of the instruction from the vm\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  // Printing disassembly\n  std::cout << std::setbase(16) << instAnalysis->address << \": \"\n            << instAnalysis->disassembly << std::endl\n            << std::setbase(10);\n  // Incrementing the instruction counter\n  (*counter)++;\n  // Signaling the VM to continue execution\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic const size_t STACK_SIZE = 0x100000; // 1MB\nstatic const QBDI::rword FAKE_RET_ADDR = 0x40000;\n\nint main(int argc, char **argv) {\n  uint8_t *fakestack = nullptr;\n  QBDI::GPRState *state;\n  int traceLevel = 0;\n  bool instrumented;\n  uint32_t counter = 0;\n\n  if (argc > 1) {\n    traceLevel = atoi(argv[1]);\n    if (traceLevel > 2) {\n      traceLevel = 2;\n    }\n  }\n\n  std::cout << \"Initializing VM ...\" << std::endl;\n  // Constructing a new QBDI vm\n  QBDI::VM *vm = new QBDI::VM();\n  // Registering count() callback to be called after every instruction\n  vm->addCodeCB(QBDI::POSTINST, count, &counter);\n\n  // Get a pointer to the GPR state of the vm\n  state = vm->getGPRState();\n  // Setup initial GPR state, this fakestack will produce a ret NULL at the end\n  // of the execution\n  QBDI::allocateVirtualStack(state, STACK_SIZE, &fakestack);\n  QBDI::simulateCall(state, FAKE_RET_ADDR);\n\n  std::cout << \"Running thedude() with trace level \" << traceLevel << \"...\"\n            << std::endl;\n  // Select which part to instrument\n  instrumented = vm->addInstrumentedModuleFromAddr((QBDI::rword)&main);\n  if (instrumented) {\n    if (traceLevel < 1) {\n      vm->removeInstrumentedRange((QBDI::rword)magicHash,\n                                  (QBDI::rword)magicHash + 32);\n    }\n    if (traceLevel < 2) {\n      vm->removeInstrumentedRange((QBDI::rword)magicPow,\n                                  (QBDI::rword)magicPow + 32);\n    }\n    // Run the DBI execution\n    vm->run((QBDI::rword)thedude, (QBDI::rword)FAKE_RET_ADDR);\n    std::cout << \"thedude ran in \" << counter << \" instructions\" << std::endl;\n  } else {\n    std::cout << \"failed to find main module...\" << std::endl;\n  }\n  delete vm;\n  QBDI::alignedFree(fakestack);\n\n  return 0;\n}\n"
  },
  {
    "path": "include/QBDI/Bitmask.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_BITMASK_H_\n#define QBDI_BITMASK_H_\n\n#ifdef __cplusplus\n#include <type_traits>\n\nnamespace QBDI {\n\ntemplate <typename Enum>\nstruct EnableBitMaskOperators {\n  static const bool enable = false;\n};\n\ntemplate <typename Enum>\ntypename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type\noperator|(Enum lhs, Enum rhs) {\n  using underlying = typename std::underlying_type<Enum>::type;\n  return static_cast<Enum>(static_cast<underlying>(lhs) |\n                           static_cast<underlying>(rhs));\n}\n\ntemplate <typename Enum>\ntypename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type &\noperator|=(Enum &lhs, Enum rhs) {\n  using underlying = typename std::underlying_type<Enum>::type;\n  lhs = static_cast<Enum>(static_cast<underlying>(lhs) |\n                          static_cast<underlying>(rhs));\n  return lhs;\n}\n\ntemplate <typename Enum>\ntypename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type\noperator&(Enum lhs, Enum rhs) {\n  using underlying = typename std::underlying_type<Enum>::type;\n  return static_cast<Enum>(static_cast<underlying>(lhs) &\n                           static_cast<underlying>(rhs));\n}\n\ntemplate <typename Enum>\ntypename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type &\noperator&=(Enum &lhs, Enum rhs) {\n  using underlying = typename std::underlying_type<Enum>::type;\n  lhs = static_cast<Enum>(static_cast<underlying>(lhs) &\n                          static_cast<underlying>(rhs));\n  return lhs;\n}\n\ntemplate <typename Enum>\ntypename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type\noperator^(Enum lhs, Enum rhs) {\n  using underlying = typename std::underlying_type<Enum>::type;\n  return static_cast<Enum>(static_cast<underlying>(lhs) ^\n                           static_cast<underlying>(rhs));\n}\n\ntemplate <typename Enum>\ntypename std::enable_if<EnableBitMaskOperators<Enum>::enable, Enum>::type &\noperator^=(Enum &lhs, Enum rhs) {\n  using underlying = typename std::underlying_type<Enum>::type;\n  lhs = static_cast<Enum>(static_cast<underlying>(lhs) ^\n                          static_cast<underlying>(rhs));\n  return lhs;\n}\n\n#define _QBDI_ENABLE_BITMASK_OPERATORS(x) \\\n  template <>                             \\\n  struct EnableBitMaskOperators<x> {      \\\n    static const bool enable = true;      \\\n  };\n\n} // namespace QBDI\n#else\n#define _QBDI_ENABLE_BITMASK_OPERATORS(x)\n#endif\n\n#endif // QBDI_BITMASK_H_\n"
  },
  {
    "path": "include/QBDI/Callback.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_CALLBACK_H_\n#define QBDI_CALLBACK_H_\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/State.h\"\n#ifdef __cplusplus\n#include <functional>\n#include <vector>\n\nnamespace QBDI {\n#endif\n\n/*! The callback results.\n */\ntypedef enum {\n  _QBDI_EI(CONTINUE) = 0,    /*!< The execution of the basic block continues. */\n  _QBDI_EI(SKIP_INST) = 1,   /*!< Available only with PREINST InstCallback.\n                              *   The instruction and the remained PREINST\n                              *   callbacks are skip. The execution continue\n                              *   with the POSTINST instruction.\n                              *\n                              *   We recommand to used this result with a low\n                              *   priority PREINST callback in order to emulate\n                              *   the instruction without skipping the POSTINST\n                              *   callback.\n                              */\n  _QBDI_EI(SKIP_PATCH) = 2,  /*!< Available only with InstCallback. The current\n                              *   instruction and the reminding callback (PRE\n                              *   and POST) are skip. The execution continues to\n                              *   the next instruction.\n                              *\n                              *   For instruction that change the instruction\n                              *   pointer (jump/call/ret), BREAK_TO_VM must be\n                              *   used insted of SKIP.\n                              *\n                              *   SKIP can break the record of MemoryAccess for\n                              *   the current instruction.\n                              */\n  _QBDI_EI(BREAK_TO_VM) = 3, /*!< The execution breaks and returns to the VM\n                              *   causing a complete reevaluation of the\n                              *   execution state. A BREAK_TO_VM is needed to\n                              *   ensure that modifications of the Program\n                              *   Counter or the program code are taken\n                              *   into account.\n                              */\n  _QBDI_EI(STOP) = 4,        /*!< Stops the execution of the program. This\n                              *   causes the run function to return early.\n                              */\n} VMAction;\n\ntypedef void *VMInstance;\n\n#ifdef __cplusplus\nclass VM;\nusing VMInstanceRef = VM *;\n#else\ntypedef VMInstance *VMInstanceRef;\n#endif\n\n/*! Instruction callback function type.\n *\n * @param[in] vm            VM instance of the callback.\n * @param[in] gprState      A structure containing the state of the\n *                          General Purpose Registers. Modifying\n *                          it affects the VM execution accordingly.\n * @param[in] fprState      A structure containing the state of the\n *                          Floating Point Registers. Modifying\n *                          it affects the VM execution accordingly.\n * @param[in] data          User defined data which can be defined when\n *                          registering the callback.\n *\n * @return                  The callback result used to signal subsequent\n *                          actions the VM needs to take.\n */\ntypedef VMAction (*InstCallback)(VMInstanceRef vm, GPRState *gprState,\n                                 FPRState *fprState, void *data);\n#ifdef __cplusplus\n/*! Instruction callback lambda type.\n *\n * @param[in] vm            VM instance of the callback.\n * @param[in] gprState      A structure containing the state of the\n *                          General Purpose Registers. Modifying\n *                          it affects the VM execution accordingly.\n * @param[in] fprState      A structure containing the state of the\n *                          Floating Point Registers. Modifying\n *                          it affects the VM execution accordingly.\n *\n * @return                  The callback result used to signal subsequent\n *                          actions the VM needs to take.\n */\ntypedef std::function<VMAction(VMInstanceRef vm, GPRState *gprState,\n                               FPRState *fprState)>\n    InstCbLambda;\n#endif\n\n/*! Position relative to an instruction.\n */\ntypedef enum {\n  _QBDI_EI(PREINST) = 0, /*!< Positioned before the instruction.*/\n  _QBDI_EI(POSTINST)     /*!< Positioned after the instruction.*/\n} InstPosition;\n\n/*! Priority of callback\n *\n * A callback with an higher priority will be call before a callback with a\n * lower priority.\n *\n * ie:\n *\n * 1. CBpre(p = 10)\n * 2. CBpre(p = 0)\n * 3. CBpre(p = -10)\n * 4. instrumented instruction\n * 5. CBpost(p = 10)\n * 6. CBpost(p = 0)\n * 7. CBpost(p = -10)\n *\n * When the MemoryAccess API is used in a callback, the priority of the callback\n * must not be greater than PRIORITY_MEMACCESS_LIMIT\n */\ntypedef enum {\n  _QBDI_EI(PRIORITY_DEFAULT) = 0, /*!< Default priority for callback */\n  _QBDI_EI(PRIORITY_MEMACCESS_LIMIT) =\n      0x1000000, /*!< Maximum priority if getInstMemoryAccess\n                  *   is used in the callback */\n} CallbackPriority;\n\ntypedef enum {\n  _QBDI_EI(NO_EVENT) = 0,\n  _QBDI_EI(SEQUENCE_ENTRY) = 1,            /*!< Triggered when the execution\n                                            * enters a sequence.\n                                            */\n  _QBDI_EI(SEQUENCE_EXIT) = 1 << 1,        /*!< Triggered when the execution\n                                            * exits from the current sequence.\n                                            */\n  _QBDI_EI(BASIC_BLOCK_ENTRY) = 1 << 2,    /*!< Triggered when the execution\n                                            * enters a basic block.\n                                            */\n  _QBDI_EI(BASIC_BLOCK_EXIT) = 1 << 3,     /*!< Triggered when the execution\n                                            * exits from the current\n                                            * basic block.\n                                            */\n  _QBDI_EI(BASIC_BLOCK_NEW) = 1 << 4,      /*!< Triggered when the execution\n                                            * enters a new (~unknown)\n                                            * basic block.\n                                            */\n  _QBDI_EI(EXEC_TRANSFER_CALL) = 1 << 5,   /*!< Triggered when the ExecBroker\n                                            * executes an execution transfer.\n                                            */\n  _QBDI_EI(EXEC_TRANSFER_RETURN) = 1 << 6, /*!< Triggered when the ExecBroker\n                                            * returns from an execution\n                                            * transfer.\n                                            */\n  _QBDI_EI(SYSCALL_ENTRY) = 1 << 7,        /*!< Not implemented.*/\n  _QBDI_EI(SYSCALL_EXIT) = 1 << 8,         /*!< Not implemented.*/\n  _QBDI_EI(SIGNAL) = 1 << 9,               /*!< Not implemented.*/\n} VMEvent;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(VMEvent)\n\n/*!\n * Structure describing the current VM state\n */\ntypedef struct {\n  VMEvent event;         /*!< The event(s) which triggered the callback (must\n                          * be checked using a mask:\n                          * event & BASIC_BLOCK_ENTRY).\n                          */\n  rword basicBlockStart; /*!< The current basic block start address which can\n                          * also be the execution transfer destination.\n                          */\n  rword basicBlockEnd;   /*!< The current basic block end address which can\n                          * also be the execution transfer destination.\n                          */\n  rword sequenceStart;   /*!< The current sequence start address which can also\n                          * be the execution transfer destination.\n                          */\n  rword sequenceEnd;     /*!< The current sequence end address which can also\n                          * be the execution transfer destination.\n                          */\n  rword lastSignal;      /*!< Not implemented.*/\n} VMState;\n\n/*! VM callback function type.\n *\n * @param[in] vm            VM instance of the callback.\n * @param[in] vmState       A structure containing the current state of the VM.\n * @param[in] gprState      A structure containing the state of the\n *                          General Purpose Registers. Modifying\n *                          it affects the VM execution accordingly.\n * @param[in] fprState      A structure containing the state of the\n *                          Floating Point Registers. Modifying\n *                          it affects the VM execution accordingly.\n * @param[in] data          User defined data which can be defined when\n *                          registering the callback.\n *\n * @return                  The callback result used to signal subsequent\n *                          actions the VM needs to take.\n */\ntypedef VMAction (*VMCallback)(VMInstanceRef vm, const VMState *vmState,\n                               GPRState *gprState, FPRState *fprState,\n                               void *data);\n#ifdef __cplusplus\n/*! VM callback lambda type.\n *\n * @param[in] vm            VM instance of the callback.\n * @param[in] vmState       A structure containing the current state of the VM.\n * @param[in] gprState      A structure containing the state of the\n *                          General Purpose Registers. Modifying\n *                          it affects the VM execution accordingly.\n * @param[in] fprState      A structure containing the state of the\n *                          Floating Point Registers. Modifying\n *                          it affects the VM execution accordingly.\n *\n * @return                  The callback result used to signal subsequent\n *                          actions the VM needs to take.\n */\ntypedef std::function<VMAction(VMInstanceRef vm, const VMState *vmState,\n                               GPRState *gprState, FPRState *fprState)>\n    VMCbLambda;\n#endif\n\nstatic const uint16_t NO_REGISTRATION = 0xFFFF;\nstatic const uint16_t NOT_FOUND = 0xFFFF;\nstatic const uint16_t ANY = 0xFFFF;\n\n/*! Memory access type (read / write / ...)\n */\ntypedef enum {\n  _QBDI_EI(MEMORY_READ) = 1,       /*!< Memory read access */\n  _QBDI_EI(MEMORY_WRITE) = 1 << 1, /*!< Memory write access */\n  _QBDI_EI(MEMORY_READ_WRITE) = 3  /*!< Memory read/write access */\n} MemoryAccessType;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(MemoryAccessType);\n\n/*! Memory access flags\n */\ntypedef enum {\n  _QBDI_EI(MEMORY_NO_FLAGS) = 0,\n  _QBDI_EI(MEMORY_UNKNOWN_SIZE) = 1 << 0,  /*!< The size of the access isn't\n                                            * known.\n                                            */\n  _QBDI_EI(MEMORY_MINIMUM_SIZE) = 1 << 1,  /*!< The given size is a minimum\n                                            * size.\n                                            */\n  _QBDI_EI(MEMORY_UNKNOWN_VALUE) = 1 << 2, /*!< The value of the access is\n                                            * unknown or hasn't been retrived.\n                                            */\n} MemoryAccessFlags;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(MemoryAccessFlags);\n\n/*! Describe a memory access\n */\ntypedef struct {\n  rword instAddress;       /*!< Address of instruction making the access */\n  rword accessAddress;     /*!< Address of accessed memory */\n  rword value;             /*!< Value read from / written to memory */\n  uint16_t size;           /*!< Size of memory access (in bytes) */\n  MemoryAccessType type;   /*!< Memory access type (READ / WRITE) */\n  MemoryAccessFlags flags; /*!< Memory access flags */\n} MemoryAccess;\n\n#ifdef __cplusplus\nstruct InstrRuleDataCBK {\n  InstPosition position; /*!< Relative position of the event callback (PREINST /\n                          * POSTINST).\n                          */\n  InstCallback cbk;      /*!< Address of the function to call when the\n                          * instruction is executed.\n                          */\n  void *data;            /*!< User defined data which will be forward to cbk */\n\n  InstCbLambda\n      lambdaCbk; /*!< Lambda callback. Replace cbk and data if not nullptr */\n\n  int priority; /*!< Priority of the callback */\n\n  InstrRuleDataCBK(InstPosition position, InstCallback cbk, void *data,\n                   int priority = PRIORITY_DEFAULT)\n      : position(position), cbk(cbk), data(data), lambdaCbk(nullptr),\n        priority(priority) {}\n  InstrRuleDataCBK(InstPosition position, const InstCbLambda &cbk,\n                   int priority = PRIORITY_DEFAULT)\n      : position(position), cbk(nullptr), data(nullptr), lambdaCbk(cbk),\n        priority(priority) {}\n  InstrRuleDataCBK(InstPosition position, InstCbLambda &&cbk,\n                   int priority = PRIORITY_DEFAULT)\n      : position(position), cbk(nullptr), data(nullptr),\n        lambdaCbk(std::move(cbk)), priority(priority) {}\n};\n\nusing InstrRuleDataVec = std::vector<InstrRuleDataCBK> *;\n#else\ntypedef void *InstrRuleDataVec;\n#endif\n\n/*! Instrumentation rule callback function type for C API.\n *\n * @param[in] vm     VM instance of the callback.\n * @param[in] inst   AnalysisType of the current instrumented Instruction.\n * @param[in] cbks   An object to add the callback to apply for this\n *                   instruction. InstCallback can be add with\n *                   qbdi_addInstrRuleData.\n * @param[in] data   User defined data which can be defined when registering\n *                   the callback.\n */\ntypedef void (*InstrRuleCallbackC)(VMInstanceRef vm, const InstAnalysis *inst,\n                                   InstrRuleDataVec cbks, void *data);\n\n#ifdef __cplusplus\n\n/*! Instrumentation rule callback function type.\n *\n * @param[in] vm     VM instance of the callback.\n * @param[in] inst   AnalysisType of the current instrumented Instruction.\n * @param[in] data   User defined data which can be defined when registering\n *                   the callback.\n *\n * @return           Return cbk to call when this instruction is run.\n */\ntypedef std::vector<InstrRuleDataCBK> (*InstrRuleCallback)(\n    VMInstanceRef vm, const InstAnalysis *inst, void *data);\n\n/*! Instrumentation rule callback lambda type.\n *\n * @param[in] vm     VM instance of the callback.\n * @param[in] inst   AnalysisType of the current instrumented Instruction.\n *\n * @return           Return cbk to call when this instruction is run.\n */\ntypedef std::function<std::vector<InstrRuleDataCBK>(VMInstanceRef vm,\n                                                    const InstAnalysis *inst)>\n    InstrRuleCbLambda;\n\n} // QBDI::\n#endif\n\n#endif\n"
  },
  {
    "path": "include/QBDI/Config.h.in",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_CONFIG_H_\n#define QBDI_CONFIG_H_\n\n#cmakedefine QBDI_ARCH_ARM     @QBDI_ARCH_ARM@\n#cmakedefine QBDI_ARCH_AARCH64 @QBDI_ARCH_AARCH64@\n#cmakedefine QBDI_ARCH_X86     @QBDI_ARCH_X86@\n#cmakedefine QBDI_ARCH_X86_64  @QBDI_ARCH_X86_64@\n\n#cmakedefine QBDI_PLATFORM_WINDOWS @QBDI_PLATFORM_WINDOWS@\n#cmakedefine QBDI_PLATFORM_LINUX   @QBDI_PLATFORM_LINUX@\n#cmakedefine QBDI_PLATFORM_ANDROID @QBDI_PLATFORM_ANDROID@\n#cmakedefine QBDI_PLATFORM_OSX     @QBDI_PLATFORM_MACOS@\n#cmakedefine QBDI_PLATFORM_MACOS   @QBDI_PLATFORM_MACOS@\n#cmakedefine QBDI_PLATFORM_IOS     @QBDI_PLATFORM_IOS@\n\n#cmakedefine QBDI_NOT_AVX_SUPPORT  @QBDI_DISABLE_AVX@\n\n#cmakedefine QBDI_BITS_32  @QBDI_BITS_32@\n#cmakedefine QBDI_BITS_64  @QBDI_BITS_64@\n\n#cmakedefine QBDI_LOG_DEBUG @QBDI_ENABLE_LOG_DEBUG@\n\n#cmakedefine QBDI_EXPORT_SYM @QBDI_EXPORT_SYM@\n\n#cmakedefine QBDI_PTRAUTH   @QBDI_PTRAUTH@\n\n#ifdef __cplusplus\nnamespace QBDI {\n\nstatic constexpr bool is_android = @QBDI_PLATFORM_ANDROID@;\nstatic constexpr bool is_linux   = @QBDI_PLATFORM_LINUX@;\nstatic constexpr bool is_osx     = @QBDI_PLATFORM_MACOS@;\nstatic constexpr bool is_macos   = @QBDI_PLATFORM_MACOS@;\nstatic constexpr bool is_ios     = @QBDI_PLATFORM_IOS@;\nstatic constexpr bool is_windows = @QBDI_PLATFORM_WINDOWS@;\n\n\nstatic constexpr bool is_arm     = @QBDI_ARCH_ARM@;\nstatic constexpr bool is_aarch64 = @QBDI_ARCH_AARCH64@;\nstatic constexpr bool is_x86     = @QBDI_ARCH_X86@;\nstatic constexpr bool is_x86_64  = @QBDI_ARCH_X86_64@;\n\nstatic constexpr bool it_bits_32 = @QBDI_BITS_32@;\nstatic constexpr bool is_bits_64 = @QBDI_BITS_64@;\n\nstatic constexpr bool has_debug_log = @QBDI_ENABLE_LOG_DEBUG@;\n}\n#endif // __cplusplus\n\n#endif // QBDI_CONFIG_H_\n"
  },
  {
    "path": "include/QBDI/Errors.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_ERRORS_H_\n#define QBDI_ERRORS_H_\n\n#include \"QBDI/Platform.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif\n\n/*! QBDI Error values\n */\ntypedef enum {\n  _QBDI_EI(INVALID_EVENTID) = 0xffffffff, /*!< Mark a returned event id as\n                                           * invalid\n                                           */\n} VMError;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // QBDI_ERRORS_H_\n"
  },
  {
    "path": "include/QBDI/InstAnalysis.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_INSTANALYSIS_H_\n#define QBDI_INSTANALYSIS_H_\n\n#include <stdbool.h>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/State.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif\n\n/*! Access type (R/W/RW) of a register operand\n */\ntypedef enum {\n  _QBDI_EI(REGISTER_UNUSED) = 0,     /*!< Unused register */\n  _QBDI_EI(REGISTER_READ) = 1,       /*!< Register read access */\n  _QBDI_EI(REGISTER_WRITE) = 1 << 1, /*!< Register write access */\n  _QBDI_EI(REGISTER_READ_WRITE) = 3, /*!< Register read/write access */\n} RegisterAccessType;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(RegisterAccessType)\n\n/*! Instruction Condition\n */\ntypedef enum {\n  _QBDI_EI(CONDITION_NONE) = 0x0, /*!< The instruction is unconditionnal */\n  // ConditionType ^ 0x1 reverse the condition, except for CONDITION_NONE\n  _QBDI_EI(CONDITION_ALWAYS) = 0x2,       /*!< The instruction is always true */\n  _QBDI_EI(CONDITION_NEVER) = 0x3,        /*!< The instruction is always false\n                                           */\n  _QBDI_EI(CONDITION_EQUALS) = 0x4,       /*!< Equals ('==') */\n  _QBDI_EI(CONDITION_NOT_EQUALS) = 0x5,   /*!< Not Equals ('!=') */\n  _QBDI_EI(CONDITION_ABOVE) = 0x6,        /*!< Above ('>' unsigned) */\n  _QBDI_EI(CONDITION_BELOW_EQUALS) = 0x7, /*!< Below or Equals ('<=' unsigned)\n                                           */\n  _QBDI_EI(CONDITION_ABOVE_EQUALS) = 0x8, /*!< Above or Equals ('>=' unsigned)\n                                           */\n  _QBDI_EI(CONDITION_BELOW) = 0x9,        /*!< Below ('<' unsigned) */\n  _QBDI_EI(CONDITION_GREAT) = 0xa,        /*!< Great ('>' signed) */\n  _QBDI_EI(CONDITION_LESS_EQUALS) = 0xb,  /*!< Less or Equals ('<=' signed) */\n  _QBDI_EI(CONDITION_GREAT_EQUALS) = 0xc, /*!< Great or Equals ('>=' signed) */\n  _QBDI_EI(CONDITION_LESS) = 0xd,         /*!< Less ('<' signed) */\n  _QBDI_EI(CONDITION_EVEN) = 0xe,         /*!< Even */\n  _QBDI_EI(CONDITION_ODD) = 0xf,          /*!< Odd */\n  _QBDI_EI(CONDITION_OVERFLOW) = 0x10,    /*!< Overflow */\n  _QBDI_EI(CONDITION_NOT_OVERFLOW) = 0x11, /*!< Not Overflow */\n  _QBDI_EI(CONDITION_SIGN) = 0x12,         /*!< Sign */\n  _QBDI_EI(CONDITION_NOT_SIGN) = 0x13,     /*!< Not Sign */\n} ConditionType;\n\n/*! Operand type\n */\ntypedef enum {\n  _QBDI_EI(OPERAND_INVALID) = 0, /*!< Invalid operand */\n  _QBDI_EI(OPERAND_IMM),         /*!< Immediate operand */\n  _QBDI_EI(OPERAND_GPR),         /*!< Register operand */\n  _QBDI_EI(OPERAND_PRED),        /*!< Predicate operand */\n  _QBDI_EI(OPERAND_FPR),         /*!< Float register operand */\n  _QBDI_EI(OPERAND_SEG),         /*!< Segment or unsupported register operand */\n} OperandType;\n\ntypedef enum {\n  _QBDI_EI(OPERANDFLAG_NONE) = 0,                  /*!< No flag */\n  _QBDI_EI(OPERANDFLAG_ADDR) = 1 << 0,             /*!< The operand is used to\n                                                    * compute an address\n                                                    */\n  _QBDI_EI(OPERANDFLAG_PCREL) = 1 << 1,            /*!< The value of the\n                                                    * operand is PC relative\n                                                    */\n  _QBDI_EI(OPERANDFLAG_UNDEFINED_EFFECT) = 1 << 2, /*!< The operand role isn't\n                                                    * fully defined\n                                                    */\n  _QBDI_EI(OPERANDFLAG_IMPLICIT) = 1 << 3,         /*!< The operand is implicit\n                                                    */\n} OperandFlag;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(OperandFlag)\n\n/*! Structure containing analysis results of an operand provided by the VM.\n */\ntypedef struct {\n  // Common fields\n  OperandType type; /*!< Operand type */\n  OperandFlag flag; /*!< Operand flag */\n  sword value;      /*!< Operand value (if immediate), or register Id */\n  uint8_t size;     /*!< Operand size (in bytes) */\n  // Register specific fields\n  uint8_t regOff;      /*!< Sub-register offset in register (in bits) */\n  int16_t regCtxIdx;   /*!< Register index in VM state (< 0 if not know) */\n  const char *regName; /*!< Register name */\n  RegisterAccessType regAccess; /*!< Register access type (r, w, rw) */\n} OperandAnalysis;\n\n/*! Instruction analysis type\n */\ntypedef enum {\n  _QBDI_EI(ANALYSIS_INSTRUCTION) = 1,      /*!< Instruction analysis (address,\n                                            * mnemonic, ...)\n                                            */\n  _QBDI_EI(ANALYSIS_DISASSEMBLY) = 1 << 1, /*!< Instruction disassembly */\n  _QBDI_EI(ANALYSIS_OPERANDS) = 1 << 2,    /*!< Instruction operands analysis */\n  _QBDI_EI(ANALYSIS_SYMBOL) = 1 << 3,      /*!< Instruction symbol */\n  _QBDI_EI(ANALYSIS_JIT) = 1 << 4,         /*!< QBDI JIT Information */\n} AnalysisType;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(AnalysisType)\n\n/*! Structure containing analysis results of an instruction provided by the VM.\n */\ntypedef struct {\n  // ANALYSIS_INSTRUCTION\n  const char *mnemonic;   /*!< LLVM mnemonic\n                           * (warning: NULL if !ANALYSIS_INSTRUCTION)\n                           */\n  rword address;          /*!< Instruction address */\n  uint32_t instSize;      /*!< Instruction size (in bytes) */\n  CPUMode cpuMode;        /*!< Instruction CPU mode */\n  bool affectControlFlow; /*!< true if instruction affects control flow */\n  bool isBranch;          /*!< true if instruction acts like a 'jump' */\n  bool isCall;            /*!< true if instruction acts like a 'call' */\n  bool isReturn;          /*!< true if instruction acts like a 'return' */\n  bool isCompare;         /*!< true if instruction is a comparison */\n  bool isPredicable;      /*!< true if instruction contains a predicate\n                           * (~is conditional)\n                           */\n  bool isMoveImm;         /*!< true if this instruction is a move immediate\n                           * (including conditional moves) instruction.\n                           */\n  bool mayLoad;       /*!< true if QBDI detects a load for this instruction */\n  bool mayStore;      /*!< true if QBDI detects a store for this instruction */\n  uint32_t loadSize;  /*!< size of the expected read access,\n                       * may be 0 with mayLoad if the size isn't\n                       * determined\n                       */\n  uint32_t storeSize; /*!< size of the expected write access,\n                       * may be 0 with mayStore if the size isn't\n                       * determined\n                       */\n  ConditionType condition; /*!< Condition associated with the instruction */\n  bool mayLoad_LLVM;       // mayLoad of 0.7.1\n  bool mayStore_LLVM;      // mayStore of 0.7.1\n  uint32_t opcode_LLVM;    // instruction opcode of LLVM (must used the exact\n                           // same version of llvm)\n  // ANALYSIS_DISASSEMBLY\n  char *disassembly; /*!< Instruction disassembly\n                      * (warning: NULL if !ANALYSIS_DISASSEMBLY) */\n  // ANALYSIS_OPERANDS\n  RegisterAccessType flagsAccess; /*!< Flag access type (noaccess, r, w, rw)\n                                   * (warning: REGISTER_UNUSED if\n                                   * !ANALYSIS_OPERANDS)\n                                   */\n  uint8_t numOperands;            /*!< Number of operands used by the\n                                   * instruction\n                                   */\n  OperandAnalysis *operands;      /*!< Structure containing analysis results\n                                   * of an operand provided by the VM.\n                                   * (warning: NULL if !ANALYSIS_OPERANDS) */\n  // ANALYSIS_SYMBOL\n  const char *symbolName; /*!< Instruction symbol\n                           * (warning: NULL if !ANALYSIS_SYMBOL or not found)\n                           */\n  uint32_t symbolOffset;  /*!< Instruction symbol offset */\n  const char *moduleName; /*!< Instruction module name\n                           * (warning: NULL if !ANALYSIS_SYMBOL or not found)\n                           */\n  // ANALYSIS_JIT\n  rword patchAddress;       /*!< Begin of the instrumentation patch for this\n                             *  instruction */\n  uint16_t patchSize;       /*!< Whole size of the instrumentation patch */\n  uint16_t patchInstOffset; /*!< Offset of the JIT instruction in the path */\n  uint16_t patchInstSize;   /*!< Size of the JIT instruction in the path */\n\n  // INTERNAL\n  uint16_t analysisType; /*!< INTERNAL: Instruction analysis type\n                          * (this should NOT be used)\n                          */\n} InstAnalysis;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // QBDI_INSTANALYSIS_H_\n"
  },
  {
    "path": "include/QBDI/Logs.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_LOGS_H_\n#define QBDI_LOGS_H_\n\n#include <stdio.h>\n#include \"QBDI/Platform.h\"\n\n#ifdef __cplusplus\n#include <string>\n#endif\n\n#ifdef __cplusplus\nnamespace QBDI {\nextern \"C\" {\n#endif\n\n// If Windows.h is included, ERROR macro may be defined\n// and will cause an error on LogPriority::ERROR\n#ifdef QBDI_PLATFORM_WINDOWS\n#ifdef ERROR\n#pragma push_macro(\"ERROR\")\n#undef ERROR\n#define QBDI_LOGS_DISABLE_ERROR\n#endif\n#endif\n\n/*! Each log has a priority (or level) which can be used to control verbosity.\n * In production builds, only Warning and Error logs are kept.\n */\ntypedef enum {\n  _QBDI_EI(DEBUG) = 0,      /*!< Debug logs */\n  _QBDI_EI(INFO),           /*!< Info logs (default) */\n  _QBDI_EI(WARNING),        /*!< Warning logs */\n  _QBDI_EI(ERROR),          /*!< Error logs */\n  _QBDI_EI(DISABLE) = 0xff, /*!< Disable logs message */\n} LogPriority;\n\n#ifdef QBDI_PLATFORM_WINDOWS\n#ifdef QBDI_LOGS_DISABLE_ERROR\n#pragma pop_macro(\"ERROR\")\n#endif\n#endif\n\n/*! Redirect logs to a file.\n *\n * @param[in] filename    the path of the file to append the log\n * @param[in] truncate    Set to true to clear the file before append the log\n */\nQBDI_EXPORT void qbdi_setLogFile(const char *filename, bool truncate);\n\n/*! Write log to the console (stderr)\n */\nQBDI_EXPORT void qbdi_setLogConsole();\n\n/*! Write log to the default location (stderr for linux, android_logger for\n * android)\n */\nQBDI_EXPORT void qbdi_setLogDefault();\n\n/*! Enable logs matching priority.\n *\n * @param[in] priority      Filter logs with greater or equal priority.\n */\nQBDI_EXPORT void qbdi_setLogPriority(LogPriority priority);\n\n#ifdef __cplusplus\n\n/*\n * C API C++ bindings\n */\n\n/*! Redirect logs to a file.\n *\n * @param[in] filename    the path of the file to append the log\n * @param[in] truncate    Set to true to clear the file before append the log\n */\nQBDI_EXPORT void setLogFile(const std::string &filename, bool truncate = false);\n\n/*! Enable logs matching priority.\n *\n * @param[in] priority      Filter logs with greater or equal priority.\n */\ninline void setLogPriority(LogPriority priority = LogPriority::INFO) {\n  return qbdi_setLogPriority(priority);\n}\n\n/*! Write log to the console (stderr)\n */\ninline void setLogConsole() { return qbdi_setLogConsole(); }\n\n/*! Write log to the default location (stderr for linux, android_logger for\n * android)\n */\ninline void setLogDefault() { return qbdi_setLogDefault(); }\n\n} // \"C\"\n\n} // QBDI::\n#endif\n\n#endif // QBDI_LOGS_H_\n"
  },
  {
    "path": "include/QBDI/Memory.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_MEMORY_H_\n#define QBDI_MEMORY_H_\n\n#include <stdarg.h>\n#include <stdbool.h>\n#include <string.h>\n\n#include \"QBDI/Platform.h\"\n#include \"QBDI/State.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\nextern \"C\" {\n#endif\n\n/*! Memory access rights.\n */\ntypedef enum {\n  QBDI_PF_NONE = 0,  /*!< No access */\n  QBDI_PF_READ = 1,  /*!< Read access */\n  QBDI_PF_WRITE = 2, /*!< Write access */\n  QBDI_PF_EXEC = 4   /*!< Execution access */\n} qbdi_Permission;\n\n/*! Map of a memory area (region).\n */\ntypedef struct {\n  rword start;                /*!< Range start value. */\n  rword end;                  /*!< Range end value (always excluded). */\n  qbdi_Permission permission; /*!< Region access rights\n                               * (PF_READ, PF_WRITE, PF_EXEC).\n                               */\n  char *name;                 /*!< Region name or path (useful when a region\n                               * is mapping a module).\n                               */\n} qbdi_MemoryMap;\n\n/*! Get a list of all the memory maps (regions) of a process.\n *\n * @param[in]  pid  The identifier of the process.\n * @param[in]  full_path  Return the full path of the module in name field\n * @param[out] size Will be set to the number of strings in the returned array.\n *\n * @return  An array of MemoryMap object.\n */\nQBDI_EXPORT qbdi_MemoryMap *qbdi_getRemoteProcessMaps(rword pid, bool full_path,\n                                                      size_t *size);\n\n/*! Get a list of all the memory maps (regions) of the current process.\n *\n * @param[in]  full_path  Return the full path of the module in name field\n * @param[out] size Will be set to the number of strings in the returned array.\n *\n * @return  An array of MemoryMap object.\n */\nQBDI_EXPORT qbdi_MemoryMap *qbdi_getCurrentProcessMaps(bool full_path,\n                                                       size_t *size);\n\n/*! Free an array of memory maps objects.\n *\n * @param[in] arr  An array of MemoryMap object.\n * @param[in] size Number of elements in the array.\n */\nQBDI_EXPORT void qbdi_freeMemoryMapArray(qbdi_MemoryMap *arr, size_t size);\n\n/*! Get a list of all the module names loaded in the process memory.\n *  If no modules are found, size is set to 0 and this function returns NULL.\n *\n * @param[out] size    Will be set to the number of strings in the returned\n *                     array.\n *\n * @return  An array of C strings, each one containing the name of a loaded\n *          module. This array needs to be free'd by the caller by repetively\n *          calling free() on each of its element then finally on the array\n *          itself.\n */\nQBDI_EXPORT char **qbdi_getModuleNames(size_t *size);\n\n/*! Allocate a block of memory of a specified sized with an aligned base\n * address.\n *\n * @param[in] size  Allocation size in bytes.\n * @param[in] align Base address alignement in bytes.\n *\n * @return  Pointer to the allocated memory or NULL in case an error was\n *          encountered.\n *\n */\nQBDI_EXPORT void *qbdi_alignedAlloc(size_t size, size_t align);\n\n/*! Free a block of aligned memory allocated with alignedAlloc.\n *\n * @param[in] ptr  Pointer to the allocated memory.\n *\n */\nQBDI_EXPORT void qbdi_alignedFree(void *ptr);\n\n/*! Allocate a new stack and setup the GPRState accordingly.\n *  The allocated stack needs to be freed with alignedFree().\n *\n *  @param[in]  ctx       GPRState which will be setup to use the new stack.\n *  @param[in]  stackSize Size of the stack to be allocated.\n *  @param[out] stack     The newly allocated stack pointer will be returned\n *                        in the variable pointed by stack.\n *\n *  @return               True if stack allocation was successfull.\n */\nQBDI_EXPORT bool qbdi_allocateVirtualStack(GPRState *ctx, uint32_t stackSize,\n                                           uint8_t **stack);\n\n/*! Simulate a call by modifying the stack and registers accordingly.\n *\n *  @param[in] ctx           GPRState where the simulated call will be setup.\n *                           The state needs to point to a valid stack for\n *                           example setup with allocateVirtualStack().\n *  @param[in] returnAddress Return address of the call to simulate.\n *  @param[in] argNum        The number of arguments in the variadic list.\n *  @param[in] ...           A variadic list of arguments.\n */\nQBDI_EXPORT void qbdi_simulateCall(GPRState *ctx, rword returnAddress,\n                                   uint32_t argNum, ...);\n\n/*! Simulate a call by modifying the stack and registers accordingly\n *  (stdarg version).\n *\n *  @param[in] ctx           GPRState where the simulated call will be setup.\n *                           The state needs to point to a valid stack\n *                           for example setup with allocateVirtualStack().\n *  @param[in] returnAddress Return address of the call to simulate.\n *  @param[in] argNum        The number of arguments in the va_list object.\n *  @param[in] ap            An stdarg va_list object.\n */\nQBDI_EXPORT void qbdi_simulateCallV(GPRState *ctx, rword returnAddress,\n                                    uint32_t argNum, va_list ap);\n\n/*! Simulate a call by modifying the stack and registers accordingly\n *  (C array version).\n *\n *  @param[in] ctx           GPRState where the simulated call will be setup.\n *                           The state needs to point to a valid stack for\n *                           example setup with allocateVirtualStack().\n *  @param[in] returnAddress Return address of the call to simulate.\n *  @param[in] argNum        The number of arguments in the array args.\n *  @param[in] args          An array or arguments.\n */\nQBDI_EXPORT void qbdi_simulateCallA(GPRState *ctx, rword returnAddress,\n                                    uint32_t argNum, const rword *args);\n\n#ifdef __cplusplus\n}\n}\n#endif\n\n#endif // QBDI_MEMORY_H_\n"
  },
  {
    "path": "include/QBDI/Memory.hpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_MEMORY_HPP_\n#define QBDI_MEMORY_HPP_\n\n#include <stdarg.h>\n#include <stdbool.h>\n#include <string.h>\n#include <string>\n#include <vector>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n/*! Memory access rights.\n */\ntypedef enum {\n  PF_NONE = 0,  /*!< No access */\n  PF_READ = 1,  /*!< Read access */\n  PF_WRITE = 2, /*!< Write access */\n  PF_EXEC = 4   /*!< Execution access */\n} Permission;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(Permission)\n\n/*! Map of a memory area (region).\n */\nstruct MemoryMap {\n\n  Range<rword> range;    /*!< A range of memory (region), delimited between\n                          * a start and an (excluded) end address.\n                          */\n  Permission permission; /*!< Region access rights\n                          * (PF_READ, PF_WRITE, PF_EXEC).\n                          */\n  std::string name;      /*!< Region name or path (useful when a region\n                          * is mapping a module).\n                          */\n\n  /* Construct a new (empty) MemoryMap.\n   */\n  MemoryMap() : range(0, 0, real_addr_t()), permission(QBDI::PF_NONE) {};\n\n  /*! Construct a new MemoryMap (given some properties).\n   *\n   * @param[in] start        Range start value.\n   * @param[in] end          Range end value (always excluded).\n   * @param[in] permission   Region access rights (PF_READ, PF_WRITE, PF_EXEC).\n   * @param[in] name         Region name (useful when a region is mapping\n   *                         a module).\n   */\n  MemoryMap(rword start, rword end, Permission permission, std::string name)\n      : range(start, end, auth_addr_t()), permission(permission), name(name) {}\n\n  /*! Construct a new MemoryMap (given some properties).\n   *\n   * @param[in] range        A range of memory (region), delimited between\n   *                         a start and an (excluded) end address.\n   * @param[in] permission   Region access rights (PF_READ, PF_WRITE, PF_EXEC).\n   * @param[in] name         Region name (useful when a region is mapping\n   *                         a module).\n   */\n  MemoryMap(Range<rword> range, Permission permission, std::string name)\n      : range(range), permission(permission), name(name) {}\n};\n\n/*! Get a list of all the memory maps (regions) of a process.\n *\n * @param[in] pid The identifier of the process.\n * @param[in] full_path  Return the full path of the module in name field\n *\n * @return  A vector of MemoryMap object.\n */\nQBDI_EXPORT std::vector<MemoryMap> getRemoteProcessMaps(QBDI::rword pid,\n                                                        bool full_path = false);\n\n/*! Get a list of all the memory maps (regions) of the current process.\n *\n * @param[in] full_path  Return the full path of the module in name field\n * @return  A vector of MemoryMap object.\n */\nQBDI_EXPORT std::vector<MemoryMap>\ngetCurrentProcessMaps(bool full_path = false);\n\n/*! Get a list of all the module names loaded in the process memory.\n *\n * @return  A vector of string of module names.\n */\nQBDI_EXPORT std::vector<std::string> getModuleNames();\n\n/*! Allocate a block of memory of a specified sized with an aligned base\n *  address.\n *\n * @param[in] size  Allocation size in bytes.\n * @param[in] align Base address alignement in bytes.\n *\n * @return  Pointer to the allocated memory or NULL in case an error was\n *          encountered.\n *\n */\nQBDI_EXPORT void *alignedAlloc(size_t size, size_t align);\n\n/*! Free a block of aligned memory allocated with alignedAlloc.\n *\n * @param[in] ptr  Pointer to the allocated memory.\n *\n */\nQBDI_EXPORT void alignedFree(void *ptr);\n\n/*! Allocate a new stack and setup the GPRState accordingly.\n *  The allocated stack needs to be freed with alignedFree().\n *\n *  @param[in]  ctx       GPRState which will be setup to use the new stack.\n *  @param[in]  stackSize Size of the stack to be allocated.\n *  @param[out] stack     The newly allocated stack pointer will be returned in\n *                        the variable pointed by stack.\n *\n *  @return               True if stack allocation was successfull.\n */\nQBDI_EXPORT bool allocateVirtualStack(GPRState *ctx, uint32_t stackSize,\n                                      uint8_t **stack);\n\n/*! Simulate a call by modifying the stack and registers accordingly\n *  (std::vector version).\n *\n *  @param[in] ctx           GPRState where the simulated call will be setup.\n *                           The state needs to point to a valid stack for\n *                           example setup with allocateVirtualStack().\n *  @param[in] returnAddress Return address of the call to simulate.\n *  @param[in] args          A list of arguments.\n */\nQBDI_EXPORT void simulateCall(GPRState *ctx, rword returnAddress,\n                              const std::vector<rword> &args = {});\n\n/*! Simulate a call by modifying the stack and registers accordingly\n *  (stdarg version).\n *\n *  @param[in] ctx           GPRState where the simulated call will be setup.\n *                           The state needs to point to a valid stack for\n *                           example setup with allocateVirtualStack().\n *  @param[in] returnAddress Return address of the call to simulate.\n *  @param[in] argNum        The number of arguments in the va_list object.\n *  @param[in] ap            An stdarg va_list object.\n */\nQBDI_EXPORT void simulateCallV(GPRState *ctx, rword returnAddress,\n                               uint32_t argNum, va_list ap);\n\n/*! Simulate a call by modifying the stack and registers accordingly\n *  (C array version).\n *\n *  @param[in] ctx           GPRState where the simulated call will be setup.\n *                           The state needs to point to a valid stack for\n *                           example setup with allocateVirtualStack().\n *  @param[in] returnAddress Return address of the call to simulate.\n *  @param[in] argNum        The number of arguments in the array args.\n *  @param[in] args          An array or arguments.\n */\nQBDI_EXPORT void simulateCallA(GPRState *ctx, rword returnAddress,\n                               uint32_t argNum, const rword *args);\n\n} // namespace QBDI\n\n#endif // QBDI_MEMORY_HPP_\n"
  },
  {
    "path": "include/QBDI/Platform.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_PLATFORM_H_\n#define QBDI_PLATFORM_H_\n\n#include \"QBDI/Config.h\"\n\n#ifdef QBDI_PLATFORM_WINDOWS\n#define QBDI_ALIGNED(n) __declspec(align(n))\n#define QBDI_NOINLINE __declspec(noinline)\n#define QBDI_NOSTACKPROTECTOR\n#define _QBDI_FORCE_USE\n#define _QBDI_UNREACHABLE() __assume(0)\n#define QBDI_DISABLE_ASAN __declspec(no_sanitize_address)\n#define QBDI_FORCE_EXPORT __declspec(dllexport)\n#else\n#define QBDI_ALIGNED(n) __attribute__((aligned(n)))\n#define QBDI_NOINLINE __attribute__((noinline))\n#define QBDI_NOSTACKPROTECTOR __attribute__((no_stack_protector))\n#define _QBDI_FORCE_USE __attribute__((__used__))\n#define _QBDI_UNREACHABLE() __builtin_unreachable()\n#define QBDI_DISABLE_ASAN __attribute__((no_sanitize_address))\n#define QBDI_FORCE_EXPORT __attribute__((visibility(\"default\")))\n#endif\n\n#ifdef QBDI_EXPORT_SYM\n#define QBDI_EXPORT QBDI_FORCE_EXPORT\n#else\n#define QBDI_EXPORT\n#endif\n\n#if defined(__has_feature)\n#if __has_feature(address_sanitizer)\n#define _QBDI_ASAN_ENABLED_\n#endif\n#endif\n\n#ifdef __cplusplus\n#define _QBDI_EI(X) X\n#else\n#define _QBDI_EI(X) QBDI_##X\n#endif\n\n#endif // QBDI_PLATFORM_H_\n"
  },
  {
    "path": "include/QBDI/PtrAuth.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_PTRAUTH_H_\n#define QBDI_PTRAUTH_H_\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\nstruct real_addr_t {\n  explicit real_addr_t() = default;\n};\n\n} // namespace QBDI\n\n#ifdef QBDI_PTRAUTH\n\n#ifndef QBDI_ARCH_AARCH64\n#error \"PTRAUTH is only implemented for AARCH64 architecture\"\n#elif defined(QBDI_PLATFORM_MACOS) || defined(QBDI_PLATFORM_IOS)\n\n#include <ptrauth.h>\nnamespace QBDI {\nstruct auth_addr_t {\n  explicit auth_addr_t() = default;\n};\n\ntemplate <typename T>\ninline T strip_ptrauth(T addr) {\n  static_assert(sizeof(T) == sizeof(void *), \"Invalid type\");\n  return reinterpret_cast<T>(strip_ptrauth(reinterpret_cast<void *>(addr)));\n}\n\ntemplate <typename T>\ninline T sign_code_ptrauth(T addr) {\n  static_assert(sizeof(T) == sizeof(void *), \"Invalid type\");\n  return reinterpret_cast<T>(sign_code_ptrauth(reinterpret_cast<void *>(addr)));\n}\n\ntemplate <>\ninline void *strip_ptrauth<void *>(void *addr) {\n  return ptrauth_strip(addr, ptrauth_key_asia);\n}\n\ntemplate <>\ninline void *sign_code_ptrauth<void *>(void *addr) {\n  return ptrauth_sign_unauthenticated(strip_ptrauth(addr), ptrauth_key_asia, 0);\n}\n\n} // namespace QBDI\n#else\n#error \"PTRAUTH is only implemented for macOS and iOS\"\n#endif\n\n#else // QBDI_PTRAUTH\n\nnamespace QBDI {\nusing auth_addr_t = real_addr_t;\n\ntemplate <typename T>\ninline T strip_ptrauth(T addr) {\n  static_assert(sizeof(T) == sizeof(void *), \"Invalid type\");\n  return addr;\n}\n\ntemplate <typename T>\ninline T sign_code_ptrauth(T addr) {\n  static_assert(sizeof(T) == sizeof(void *), \"Invalid type\");\n  return addr;\n}\n\n} // namespace QBDI\n#endif\n\n#endif // QBDI_PTRAUTH_H_\n"
  },
  {
    "path": "include/QBDI/Range.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_RANGE_H_\n#define QBDI_RANGE_H_\n\n#include <algorithm>\n#include <ostream>\n#include <vector>\n\n#include \"QBDI/PtrAuth.h\"\n\nnamespace QBDI {\n\ntemplate <typename T>\nclass Range {\n\nprivate:\n  T min_(const T a, const T b) const { return a < b ? a : b; }\n\n  T max_(const T a, const T b) const { return a > b ? a : b; }\n\n  T _start; /*!< Range start value. */\n  T _end;   /*!< Range end value (always excluded). */\n\npublic:\n  inline T start() const { return _start; }\n  inline T end() const { return _end; }\n  inline void setStart(const T start) {\n    _start = start;\n    if (_end < _start)\n      _end = _start;\n  }\n  inline void setEnd(const T end) {\n    _end = end;\n    if (_end < _start)\n      _start = _end;\n  }\n\n  /*! Construct a new range from not authenticated pointer.\n   * @param[in] start   Range start value.\n   * @param[in] end     Range end value (excluded).\n   * @param[in] mark    Real address mark\n   */\n  Range(const T start, const T end, const real_addr_t &) {\n    if (start < end) {\n      this->_start = start;\n      this->_end = end;\n    } else {\n      this->_end = this->_start = start;\n    }\n  }\n\n#ifdef QBDI_PTRAUTH\n  /*! Construct a new range from authenticated pointer.\n   * @param[in] start   Range start value.\n   * @param[in] end     Range end value (excluded).\n   * @param[in] mark    Auth address mark\n   */\n  Range(const T start, const T end, const auth_addr_t &)\n      : Range(strip_ptrauth(start), strip_ptrauth(end), real_addr_t()) {}\n#endif\n\n  /*! Construct a new range.\n   * @param[in] start  Range start value.\n   * @param[in] end  Range end value (excluded).\n   */\n  Range(const T start, const T end) : Range(start, end, auth_addr_t()) {}\n\n  /*! Return the total length of a range.\n   */\n  T size() const { return end() - start(); }\n\n  /*! Return True if two ranges are equal (same boundaries).\n   *\n   * @param[in] r  Range to check.\n   *\n   * @return  True if equal.\n   */\n  bool operator==(const Range &r) const {\n    return r.start() == start() && r.end() == end();\n  }\n\n  /*! Return True if an value is inside current range boundaries.\n   *\n   * @param[in] t  Value to check.\n   *\n   * @return  True if contained.\n   */\n  bool contains(const T t) const { return (start() <= t) && (t < end()); }\n\n  /*! Return True if a range is inside current range boundaries.\n   *\n   * @param[in] r  Range to check.\n   *\n   * @return  True if contained.\n   */\n  bool contains(const Range<T> &r) const {\n    return (start() <= r.start()) && (r.end() <= end());\n  }\n\n  /*! Return True if a range is overlapping current range lower\n   *  or/and upper boundary.\n   *\n   * @param[in] r  Range to check.\n   *\n   * @return  True if overlapping.\n   */\n  bool overlaps(const Range<T> &r) const {\n    return start() < r.end() && r.start() < end();\n  }\n\n  /*! Pretty print a range\n   *\n   * @param[in] os  An output stream.\n   */\n  void display(std::ostream &os) const {\n    os << \"(0x\" << std::hex << start() << \", 0x\" << end() << \")\";\n  }\n\n  /*! Return the intersection of two ranges.\n   *\n   * @param[in] r  Range to intersect with current range.\n   *\n   * @return  A new range.\n   */\n  Range<T> intersect(const Range<T> &r) const {\n    return Range<T>(max_(start(), r.start()), min_(end(), r.end()),\n                    real_addr_t());\n  }\n};\n\ntemplate <typename T>\nstd::ostream &operator<<(std::ostream &os, const Range<T> &obj) {\n  obj.display(os);\n  return os;\n}\n\ntemplate <typename T>\nclass RangeSet {\n\nprivate:\n  std::vector<Range<T>> ranges;\n\npublic:\n  RangeSet() {}\n\n  const std::vector<Range<T>> &getRanges() const { return ranges; }\n\n  T size() const {\n    T sum = 0;\n    for (const Range<T> &r : ranges) {\n      sum += r.size();\n    }\n    return sum;\n  }\n\n  const Range<T> *getElementRange(const T &t) const {\n    auto lower = std::lower_bound(\n        ranges.cbegin(), ranges.cend(), t,\n        [](const Range<T> &r, const T &value) { return r.end() <= value; });\n    if (lower == ranges.cend() || (!lower->contains(t))) {\n      return nullptr;\n    } else {\n      return &*lower;\n    }\n  }\n\n  bool contains(const T &t) const { return getElementRange(t) != nullptr; }\n\n  bool contains(const Range<T> &t) const {\n    if (t.end() <= t.start()) {\n      return true;\n    }\n    auto lower = std::lower_bound(\n        ranges.cbegin(), ranges.cend(), t.start(),\n        [](const Range<T> &r, const T &value) { return r.end() <= value; });\n    if (lower == ranges.cend()) {\n      return false;\n    } else {\n      return lower->contains(t);\n    }\n  }\n\n  bool overlaps(const Range<T> &t) const {\n    if (t.end() <= t.start()) {\n      return true;\n    }\n    auto lower = std::lower_bound(\n        ranges.cbegin(), ranges.cend(), t.start(),\n        [](const Range<T> &r, const T &value) { return r.end() <= value; });\n    if (lower == ranges.cend()) {\n      return false;\n    } else {\n      return lower->overlaps(t);\n    }\n  }\n\n  void add(const Range<T> &t) {\n    // Exception for empty ranges\n    if (t.end() <= t.start()) {\n      return;\n    }\n\n    // Find lower element in sorted range list\n    auto lower = std::lower_bound(\n        ranges.begin(), ranges.end(), t.start(),\n        [](const Range<T> &r, const T &value) { return r.end() < value; });\n\n    // if no range match, push the new range at the end of the list\n    if (lower == ranges.end()) {\n      ranges.push_back(t);\n      return;\n    }\n\n    // if t is strictly before lower, just insert it before lower\n    if (t.end() < lower->start()) {\n      ranges.insert(lower, t);\n      return;\n    }\n\n    // lower and t either intersect, or t is right before lower\n    // Extend the start of lower\n    if (t.start() < lower->start()) {\n      lower->setStart(t.start());\n    }\n    // Extend the end of lower\n    if (lower->end() < t.end()) {\n      lower->setEnd(t.end());\n    }\n\n    // we extend the end of lower, but we must verify that the new end isn't\n    // overlapsed nested element\n    auto endEraseList = lower + 1;\n    while (endEraseList != ranges.end()) {\n      if (lower->end() < endEraseList->start()) {\n        break;\n      }\n      if (lower->end() < endEraseList->end()) {\n        lower->setEnd(endEraseList->end());\n      }\n      endEraseList++;\n    }\n    ranges.erase(lower + 1, endEraseList);\n  }\n\n  void add(const RangeSet<T> &t) {\n    for (const Range<T> &r : t.ranges) {\n      add(r);\n    }\n  }\n\n  void remove(const Range<T> &t) {\n    // Exception for empty ranges\n    if (t.end() <= t.start()) {\n      return;\n    }\n\n    // Find lower element in sorted range list\n    auto lower = std::lower_bound(\n        ranges.begin(), ranges.end(), t.start(),\n        [](const Range<T> &r, const T &value) { return r.end() <= value; });\n\n    // if no range match, do nothing\n    if (lower == ranges.end()) {\n      return;\n    }\n\n    // if t is before lower, do nothing\n    if (t.end() <= lower->start()) {\n      return;\n    }\n    // lower and t intersect\n\n    // managed case where t begin inside a range\n    if (lower->start() < t.start()) {\n\n      if (t.end() < lower->end()) {\n        // t is a part of lower, but with extra both at the start and the end\n        // -> split lower in two\n        Range<T> preRange = {lower->start(), t.start(), real_addr_t()};\n        lower->setStart(t.end());\n        ranges.insert(lower, preRange);\n        return;\n      }\n\n      // lower begins before t, but end inside t (or has the same end).\n      // => reduce lower\n      lower->setEnd(t.start());\n\n      // lower is not longuer inside t, go to the next element;\n      lower++;\n    }\n\n    // Erase all range that are inside t\n    auto beginEraseList = lower;\n    while (lower != ranges.end()) {\n      // lower is after t, exit\n      if (t.end() <= lower->start()) {\n        break;\n      }\n\n      // lower overlaps t but the end\n      if (t.end() < lower->end()) {\n        lower->setStart(t.end());\n        break;\n      }\n\n      // lower is included in t, erase it at the end\n      lower++;\n    }\n    ranges.erase(beginEraseList, lower);\n  }\n\n  void remove(const RangeSet<T> &t) {\n    for (const Range<T> &r : t.ranges) {\n      remove(r);\n    }\n  }\n\n  void intersect(const RangeSet<T> &t) {\n    RangeSet<T> intersected;\n    auto itThis = ranges.cbegin();\n    auto itOther = t.ranges.cbegin();\n    while (itThis != ranges.cend() && itOther != t.ranges.cend()) {\n      if (itThis->overlaps(*itOther)) {\n        intersected.add(itThis->intersect(*itOther));\n      }\n      // select the iterator to step\n      if (itThis->end() < itOther->end()) {\n        itThis++;\n      } else if (itThis->end() == itOther->end()) {\n        itThis++;\n        itOther++;\n      } else {\n        itOther++;\n      }\n    }\n    ranges.swap(intersected.ranges);\n  }\n\n  void intersect(const Range<T> &t) {\n    auto lower = std::lower_bound(\n        ranges.cbegin(), ranges.cend(), t.start(),\n        [](const Range<T> &r, const T &value) { return r.end() <= value; });\n\n    if (lower == ranges.cend()) {\n      clear();\n      return;\n    }\n\n    RangeSet<T> intersected;\n    while (lower != ranges.cend() && t.overlaps(*lower)) {\n      intersected.add(t.intersect(*lower));\n      lower++;\n    }\n\n    ranges.swap(intersected.ranges);\n  }\n\n  void clear() { ranges.clear(); }\n\n  void display(std::ostream &os) const {\n    os << \"[\";\n    for (const Range<T> &r : ranges) {\n      r.display(os);\n      os << \", \";\n    }\n    os << \"]\";\n  }\n\n  bool operator==(const RangeSet &r) const {\n    const std::vector<Range<T>> &ranges = r.ranges;\n\n    if (this->ranges.size() != ranges.size()) {\n      return false;\n    }\n\n    for (size_t i = 0; i < ranges.size(); i++) {\n      if (!(this->ranges[i] == ranges[i])) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n};\n\ntemplate <typename T>\nstd::ostream &operator<<(std::ostream &os, const RangeSet<T> &obj) {\n  obj.display(os);\n  return os;\n}\n\n} // namespace QBDI\n\n#endif // QBDI_RANGE_H_\n"
  },
  {
    "path": "include/QBDI/VM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_VM_H_\n#define QBDI_VM_H_\n\n#include <cstdarg>\n#include <forward_list>\n#include <memory>\n#include <stdint.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Callback.h\"\n#include \"QBDI/Errors.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n// Forward declaration of engine class\nclass Engine;\n// Forward declaration of private memCBInfo\nstruct MemCBInfo;\n// Forward declaration of private InstrCBInfo\nstruct InstrCBInfo;\n\nclass VM {\nprivate:\n  // Private internal engine\n  std::unique_ptr<Engine> engine;\n  uint8_t memoryLoggingLevel;\n  std::unique_ptr<std::vector<std::pair<uint32_t, MemCBInfo>>> memCBInfos;\n  uint32_t memCBID;\n  uint32_t memReadGateCBID;\n  uint32_t memWriteGateCBID;\n  std::unique_ptr<\n      std::vector<std::pair<uint32_t, std::unique_ptr<InstrCBInfo>>>>\n      instrCBInfos;\n  std::forward_list<std::pair<uint32_t, VMCbLambda>> vmCBData;\n  std::forward_list<std::pair<uint32_t, InstCbLambda>> instCBData;\n  std::forward_list<std::pair<uint32_t, InstrRuleCbLambda>> instrRuleCBData;\n\n  uint32_t backupErrno;\n\npublic:\n  /*! Construct a new VM for a given CPU with specific attributes\n   *\n   * @param[in] cpu    The name of the CPU\n   * @param[in] mattrs A list of additional attributes\n   * @param[in] opts   The options to enable in the VM\n   */\n  QBDI_EXPORT VM(const std::string &cpu = \"\",\n                 const std::vector<std::string> &mattrs = {},\n                 Options opts = Options::NO_OPT);\n\n  QBDI_EXPORT ~VM();\n\n  /*! Move constructors.\n   *  All the cache is keep.\n   *  All registered callbacks will be called with the new pointer of the VM.\n   *\n   * @param[in] vm     The VM to move\n   */\n  QBDI_EXPORT VM(VM &&vm);\n\n  /*! Move assignment operator\n   *  All the cache is keep.\n   *  All registered callbacks will be called with the new pointer of the VM.\n   *  This operator mustn't be called when the target VM runs.\n   *\n   * @param[in] vm     The VM to move\n   */\n  QBDI_EXPORT VM &operator=(VM &&vm);\n\n  /*! Copy constructors\n   *  The state and the configuration is copied. The cache isn't duplicate.\n   *  The assigned VM begin with an empty cache.\n   *\n   * @param[in] vm     The VM to copy\n   */\n  QBDI_EXPORT VM(const VM &vm);\n\n  /*! Copy assignment operator\n   *  The state and the configuration is copied. The cache isn't duplicate.\n   *  The assigned VM begin with an empty cache.\n   *  This operator mustn't be called when the target VM runs.\n   *\n   * @param[in] vm     The VM to copy\n   */\n  QBDI_EXPORT VM &operator=(const VM &vm);\n\n  // delete const move constructor and const move assignment operator\n  QBDI_EXPORT VM(const VM &&vm) = delete;\n  QBDI_EXPORT VM &operator=(const VM &&vm) = delete;\n\n  /*! Obtain the current general purpose register state.\n   *\n   * @return A structure containing the GPR state.\n   */\n  QBDI_EXPORT GPRState *getGPRState() const;\n\n  /*! Obtain the current floating point register state.\n   *\n   * @return A structure containing the FPR state.\n   */\n  QBDI_EXPORT FPRState *getFPRState() const;\n\n  /*! Obtain the backuped value of errno, if the option\n   * OPT_DISABLE_ERRNO_BACKUP is not enable.\n   *\n   * @return the backupped value of errno\n   */\n  QBDI_EXPORT uint32_t getErrno() { return backupErrno; }\n\n  /*! Set the GPR state\n   *\n   * @param[in] gprState A structure containing the GPR state.\n   */\n  QBDI_EXPORT void setGPRState(const GPRState *gprState);\n\n  /*! Set the FPR state\n   *\n   * @param[in] fprState A structure containing the FPR state.\n   */\n  QBDI_EXPORT void setFPRState(const FPRState *fprState);\n\n  /*! Set the backuped value of errno, if the option\n   * OPT_DISABLE_ERRNO_BACKUP is not enable.\n   *\n   * @param[in] backupErrno_ the value to set\n   */\n  QBDI_EXPORT void setErrno(uint32_t backupErrno_) {\n    backupErrno = backupErrno_;\n  }\n\n  /*! Get the current Options of the VM\n   */\n  QBDI_EXPORT Options getOptions() const;\n\n  /*! Set the Options of the VM\n   *  This method mustn't be called when the VM runs.\n   *\n   * @param[in] options  the new options of the VM\n   *\n   * If the new options is different that the current ones, the cache will be\n   * clear.\n   */\n  QBDI_EXPORT void setOptions(Options options);\n\n  /*! Add an address range to the set of instrumented address ranges.\n   *\n   * @param[in] start  Start address of the range (included).\n   * @param[in] end    End address of the range (excluded).\n   */\n  QBDI_EXPORT void addInstrumentedRange(rword start, rword end);\n\n  /*! Add the executable address ranges of a module to the set of instrumented\n   * address ranges.\n   *\n   * @param[in] name  The module's name.\n   *\n   * @return  True if at least one range was added to the instrumented ranges.\n   */\n  QBDI_EXPORT bool addInstrumentedModule(const std::string &name);\n\n  /*! Add the executable address ranges of a module to the set of instrumented\n   * address ranges using an address belonging to the module.\n   *\n   * @param[in] addr  An address contained by module's range.\n   *\n   * @return  True if at least one range was added to the instrumented ranges.\n   */\n  QBDI_EXPORT bool addInstrumentedModuleFromAddr(rword addr);\n\n  /*! Adds all the executable memory maps to the instrumented range set.\n   * @return  True if at least one range was added to the instrumented ranges.\n   */\n  QBDI_EXPORT bool instrumentAllExecutableMaps();\n\n  /*! Remove an address range from the set of instrumented address ranges.\n   *\n   * @param[in] start  Start address of the range (included).\n   * @param[in] end    End address of the range (excluded).\n   */\n  QBDI_EXPORT void removeInstrumentedRange(rword start, rword end);\n\n  /*! Remove the executable address ranges of a module from the set of\n   * instrumented address ranges.\n   *\n   * @param[in] name  The module's name.\n   *\n   * @return  True if at least one range was removed from the instrumented\n   * ranges.\n   */\n  QBDI_EXPORT bool removeInstrumentedModule(const std::string &name);\n\n  /*! Remove the executable address ranges of a module from the set of\n   * instrumented address ranges using an address belonging to the module.\n   *\n   * @param[in] addr  An address contained by module's range.\n   *\n   * @return  True if at least one range was removed from the instrumented\n   * ranges.\n   */\n  QBDI_EXPORT bool removeInstrumentedModuleFromAddr(rword addr);\n\n  /*! Remove all instrumented ranges.\n   */\n  QBDI_EXPORT void removeAllInstrumentedRanges();\n\n  /*! Start the execution by the DBI.\n   *  This method mustn't be called if the VM already runs.\n   *\n   * @param[in] start  Address of the first instruction to execute.\n   * @param[in] stop   Stop the execution when this instruction is reached.\n   *\n   * @return  True if at least one block has been executed.\n   */\n  QBDI_EXPORT bool run(rword start, rword stop);\n\n  /*! Call a function using the DBI (and its current state).\n   *  This method mustn't be called if the VM already runs.\n   *\n   * @param[in] [retval]   Pointer to the returned value (optional).\n   * @param[in] function   Address of the function start instruction.\n   * @param[in] args       A list of arguments.\n   *\n   * @return  True if at least one block has been executed.\n   *\n   * @details Example:\n   *\n   *     // perform (with QBDI) a call similar to (*funcPtr)(42);\n   *     uint8_t *fakestack = nullptr;\n   *     QBDI::VM *vm = new QBDI::VM();\n   *     QBDI::GPRState *state = vm->getGPRState();\n   *     QBDI::allocateVirtualStack(state, 0x1000000, &fakestack);\n   *     vm->addInstrumentedModuleFromAddr(funcPtr);\n   *     rword retVal;\n   *     vm->call(&retVal, funcPtr, {42});\n   *     QBDI::alignedFree(fakestack);\n   *\n   */\n  QBDI_EXPORT bool call(rword *retval, rword function,\n                        const std::vector<rword> &args = {});\n\n  /*! Call a function using the DBI (and its current state).\n   *  This method mustn't be called if the VM already runs.\n   *\n   * @param[in] [retval]   Pointer to the returned value (optional).\n   * @param[in] function   Address of the function start instruction.\n   * @param[in] argNum     The number of arguments in the array of arguments.\n   * @param[in] args       An array of arguments.\n   *\n   * @return  True if at least one block has been executed.\n   */\n  QBDI_EXPORT bool callA(rword *retval, rword function, uint32_t argNum,\n                         const rword *args);\n\n  /*! Call a function using the DBI (and its current state).\n   *  This method mustn't be called if the VM already runs.\n   *\n   * @param[in] [retval]   Pointer to the returned value (optional).\n   * @param[in] function   Address of the function start instruction.\n   * @param[in] argNum     The number of arguments in the variadic list.\n   * @param[in] ap         An stdarg va_list object.\n   *\n   * @return  True if at least one block has been executed.\n   */\n  QBDI_EXPORT bool callV(rword *retval, rword function, uint32_t argNum,\n                         va_list ap);\n\n  /*! Switch the stack and call a function using the DBI (and its current\n   *  state).\n   *  This method will allocate a new stack and switch to this stack. The\n   *  remaining space on the current stack will be use by the called method.\n   *  This method mustn't be called if the VM already runs.\n   *  The stack pointer in the state must'nt be used after the end of this\n   *  method.\n   *\n   * @param[in] [retval]   Pointer to the returned value (optional).\n   * @param[in] function   Address of the function start instruction.\n   * @param[in] args       A list of arguments.\n   * @param[in] stackSize  The size of the stack for the engine.\n   *\n   * @return  True if at least one block has been executed.\n   *\n   * @details Example:\n   *\n   *     // perform (with QBDI) a call similar to (*funcPtr)(42);\n   *     QBDI::VM *vm = new QBDI::VM();\n   *     QBDI::GPRState *state = vm->getGPRState();\n   *     vm->addInstrumentedModuleFromAddr(funcPtr);\n   *     rword retVal;\n   *     vm->switchStackAndCall(&retVal, funcPtr, {42});\n   *\n   */\n\n  QBDI_EXPORT bool switchStackAndCall(rword *retval, rword function,\n                                      const std::vector<rword> &args = {},\n                                      uint32_t stackSize = 0x20000);\n\n  /*! Switch the stack and call a function using the DBI (and its current\n   *  state).\n   *  This method mustn't be called if the VM already runs.\n   *  The stack pointer in the state must'nt be used after the end of this\n   *  method.\n   *\n   * @param[in] [retval]   Pointer to the returned value (optional).\n   * @param[in] function   Address of the function start instruction.\n   * @param[in] argNum     The number of arguments in the array of arguments.\n   * @param[in] args       An array of arguments.\n   * @param[in] stackSize  The size of the stack for the engine.\n   *\n   * @return  True if at least one block has been executed.\n   */\n  QBDI_EXPORT bool switchStackAndCallA(rword *retval, rword function,\n                                       uint32_t argNum, const rword *args,\n                                       uint32_t stackSize = 0x20000);\n\n  /*! Switch the stack and call a function using the DBI (and its current\n   *  state).\n   *  This method mustn't be called if the VM already runs.\n   *  The stack pointer in the state must'nt be used after the end of this\n   *  method.\n   *\n   * @param[in] [retval]   Pointer to the returned value (optional).\n   * @param[in] function   Address of the function start instruction.\n   * @param[in] argNum     The number of arguments in the variadic list.\n   * @param[in] ap         An stdarg va_list object.\n   * @param[in] stackSize  The size of the stack for the engine.\n   *\n   * @return  True if at least one block has been executed.\n   */\n  QBDI_EXPORT bool switchStackAndCallV(rword *retval, rword function,\n                                       uint32_t argNum, va_list ap,\n                                       uint32_t stackSize = 0x20000);\n\n  /*! Add a custom instrumentation rule to the VM.\n   *\n   * @param[in] cbk       A function pointer to the callback\n   * @param[in] type      Analyse type needed for this instruction function\n   *                      pointer to the callback\n   * @param[in] data      User defined data passed to the callback.\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addInstrRule(InstrRuleCallback cbk, AnalysisType type,\n                                    void *data);\n\n  // register C like InstrRuleCallback\n  QBDI_EXPORT uint32_t addInstrRule(InstrRuleCallbackC cbk, AnalysisType type,\n                                    void *data);\n\n  /*! Add a custom instrumentation rule to the VM.\n   *\n   * @param[in] cbk       A lambda function to the callback\n   * @param[in] type      Analyse type needed for this instruction function\n   *                      pointer to the callback\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addInstrRule(const InstrRuleCbLambda &cbk,\n                                    AnalysisType type);\n  QBDI_EXPORT uint32_t addInstrRule(InstrRuleCbLambda &&cbk, AnalysisType type);\n\n  /*! Add a custom instrumentation rule to the VM on a specify range\n   *\n   * @param[in] start     Begin of the range of address where apply the rule\n   * @param[in] end       End of the range of address where apply the rule\n   * @param[in] cbk       A function pointer to the callback\n   * @param[in] type      Analyse type needed for this instruction function\n   *                      pointer to the callback\n   * @param[in] data      User defined data passed to the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addInstrRuleRange(rword start, rword end,\n                                         InstrRuleCallback cbk,\n                                         AnalysisType type, void *data);\n\n  // register C like InstrRuleCallback\n  QBDI_EXPORT uint32_t addInstrRuleRange(rword start, rword end,\n                                         InstrRuleCallbackC cbk,\n                                         AnalysisType type, void *data);\n\n  /*! Add a custom instrumentation rule to the VM on a specify range\n   *\n   * @param[in] start     Begin of the range of address where apply the rule\n   * @param[in] end       End of the range of address where apply the rule\n   * @param[in] cbk       A lambda function to the callback\n   * @param[in] type      Analyse type needed for this instruction function\n   *                      pointer to the callback\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addInstrRuleRange(rword start, rword end,\n                                         const InstrRuleCbLambda &cbk,\n                                         AnalysisType type);\n  QBDI_EXPORT uint32_t addInstrRuleRange(rword start, rword end,\n                                         InstrRuleCbLambda &&cbk,\n                                         AnalysisType type);\n\n  /*! Add a custom instrumentation rule to the VM on a specify set of range\n   *\n   * @param[in] range     Range of address where apply the rule\n   * @param[in] cbk       A function pointer to the callback\n   * @param[in] type      Analyse type needed for this instruction function\n   *                      pointer to the callback\n   * @param[in] data      User defined data passed to the callback.\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addInstrRuleRangeSet(RangeSet<rword> range,\n                                            InstrRuleCallback cbk,\n                                            AnalysisType type, void *data);\n\n  /*! Add a custom instrumentation rule to the VM on a specify set of range\n   *\n   * @param[in] range     Range of address where apply the rule\n   * @param[in] cbk       A lambda function to the callback\n   * @param[in] type      Analyse type needed for this instruction function\n   *                      pointer to the callback\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addInstrRuleRangeSet(RangeSet<rword> range,\n                                            const InstrRuleCbLambda &cbk,\n                                            AnalysisType type);\n  QBDI_EXPORT uint32_t addInstrRuleRangeSet(RangeSet<rword> range,\n                                            InstrRuleCbLambda &&cbk,\n                                            AnalysisType type);\n\n  /*! Register a callback event if the instruction matches the mnemonic.\n   *\n   * @param[in] mnemonic   Mnemonic to match.\n   * @param[in] pos        Relative position of the event callback\n   *                       (PREINST / POSTINST).\n   * @param[in] cbk        A function pointer to the callback.\n   * @param[in] data       User defined data passed to the callback.\n   * @param[in] priority   The priority of the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMnemonicCB(const char *mnemonic, InstPosition pos,\n                                     InstCallback cbk, void *data,\n                                     int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback event if the instruction matches the mnemonic.\n   *\n   * @param[in] mnemonic   Mnemonic to match.\n   * @param[in] pos        Relative position of the event callback\n   *                       (PREINST / POSTINST).\n   * @param[in] cbk        A lambda function to the callback\n   * @param[in] priority   The priority of the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMnemonicCB(const char *mnemonic, InstPosition pos,\n                                     const InstCbLambda &cbk,\n                                     int priority = PRIORITY_DEFAULT);\n  QBDI_EXPORT uint32_t addMnemonicCB(const char *mnemonic, InstPosition pos,\n                                     InstCbLambda &&cbk,\n                                     int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback event for every instruction executed.\n   *\n   * @param[in] pos        Relative position of the event callback\n   *                       (PREINST / POSTINST).\n   * @param[in] cbk        A function pointer to the callback.\n   * @param[in] data       User defined data passed to the callback.\n   * @param[in] priority   The priority of the callback.\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addCodeCB(InstPosition pos, InstCallback cbk, void *data,\n                                 int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback event for every instruction executed.\n   *\n   * @param[in] pos        Relative position of the event callback\n   *                       (PREINST / POSTINST).\n   * @param[in] cbk        A lambda function to the callback\n   * @param[in] priority   The priority of the callback.\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addCodeCB(InstPosition pos, const InstCbLambda &cbk,\n                                 int priority = PRIORITY_DEFAULT);\n  QBDI_EXPORT uint32_t addCodeCB(InstPosition pos, InstCbLambda &&cbk,\n                                 int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback for when a specific address is executed.\n   *\n   * @param[in] address  Code address which will trigger the callback.\n   * @param[in] pos      Relative position of the callback (PREINST / POSTINST).\n   * @param[in] cbk      A function pointer to the callback.\n   * @param[in] data     User defined data passed to the callback.\n   * @param[in] priority The priority of the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addCodeAddrCB(rword address, InstPosition pos,\n                                     InstCallback cbk, void *data,\n                                     int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback for when a specific address is executed.\n   *\n   * @param[in] address  Code address which will trigger the callback.\n   * @param[in] pos      Relative position of the callback (PREINST / POSTINST).\n   * @param[in] cbk      A lambda function to the callback\n   * @param[in] priority The priority of the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addCodeAddrCB(rword address, InstPosition pos,\n                                     const InstCbLambda &cbk,\n                                     int priority = PRIORITY_DEFAULT);\n  QBDI_EXPORT uint32_t addCodeAddrCB(rword address, InstPosition pos,\n                                     InstCbLambda &&cbk,\n                                     int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback for when a specific address range is executed.\n   *\n   * @param[in] start    Start of the address range which will trigger\n   *                     the callback.\n   * @param[in] end      End of the address range which will trigger\n   *                     the callback.\n   * @param[in] pos      Relative position of the callback (PREINST / POSTINST).\n   * @param[in] cbk      A function pointer to the callback.\n   * @param[in] data     User defined data passed to the callback.\n   * @param[in] priority The priority of the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addCodeRangeCB(rword start, rword end, InstPosition pos,\n                                      InstCallback cbk, void *data,\n                                      int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback for when a specific address range is executed.\n   *\n   * @param[in] start    Start of the address range which will trigger\n   *                     the callback.\n   * @param[in] end      End of the address range which will trigger\n   *                     the callback.\n   * @param[in] pos      Relative position of the callback (PREINST / POSTINST).\n   * @param[in] cbk      A lambda function to the callback\n   * @param[in] priority The priority of the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addCodeRangeCB(rword start, rword end, InstPosition pos,\n                                      const InstCbLambda &cbk,\n                                      int priority = PRIORITY_DEFAULT);\n  QBDI_EXPORT uint32_t addCodeRangeCB(rword start, rword end, InstPosition pos,\n                                      InstCbLambda &&cbk,\n                                      int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback event for every memory access matching the type\n   * bitfield made by the instructions.\n   *\n   * @param[in] type       A mode bitfield: either QBDI::MEMORY_READ,\n   *                       QBDI::MEMORY_WRITE or both (QBDI::MEMORY_READ_WRITE).\n   * @param[in] cbk        A function pointer to the callback.\n   * @param[in] data       User defined data passed to the callback.\n   * @param[in] priority   The priority of the callback.\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMemAccessCB(MemoryAccessType type, InstCallback cbk,\n                                      void *data,\n                                      int priority = PRIORITY_DEFAULT);\n\n  /*! Register a callback event for every memory access matching the type\n   * bitfield made by the instructions.\n   *\n   * @param[in] type       A mode bitfield: either QBDI::MEMORY_READ,\n   *                       QBDI::MEMORY_WRITE or both (QBDI::MEMORY_READ_WRITE).\n   * @param[in] cbk        A lambda function to the callback\n   * @param[in] priority   The priority of the callback.\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMemAccessCB(MemoryAccessType type,\n                                      const InstCbLambda &cbk,\n                                      int priority = PRIORITY_DEFAULT);\n  QBDI_EXPORT uint32_t addMemAccessCB(MemoryAccessType type, InstCbLambda &&cbk,\n                                      int priority = PRIORITY_DEFAULT);\n\n  /*! Add a virtual callback which is triggered for any memory access at a\n   * specific address matching the access type. Virtual callbacks are called via\n   * callback forwarding by a gate callback triggered on every memory access.\n   * This incurs a high performance cost. The callback has the default priority.\n   *\n   * @param[in] address  Code address which will trigger the callback.\n   * @param[in] type     A mode bitfield: either QBDI::MEMORY_READ,\n   *                     QBDI::MEMORY_WRITE or both (QBDI::MEMORY_READ_WRITE).\n   * @param[in] cbk      A function pointer to the callback.\n   * @param[in] data     User defined data passed to the callback.\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMemAddrCB(rword address, MemoryAccessType type,\n                                    InstCallback cbk, void *data);\n\n  /*! Add a virtual callback which is triggered for any memory access at a\n   * specific address matching the access type. Virtual callbacks are called via\n   * callback forwarding by a gate callback triggered on every memory access.\n   * This incurs a high performance cost. The callback has the default priority.\n   *\n   * @param[in] address  Code address which will trigger the callback.\n   * @param[in] type     A mode bitfield: either QBDI::MEMORY_READ,\n   *                     QBDI::MEMORY_WRITE or both (QBDI::MEMORY_READ_WRITE).\n   * @param[in] cbk      A lambda function to the callback\n   *\n   * @return The id of the registered instrumentation\n   * (or VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMemAddrCB(rword address, MemoryAccessType type,\n                                    const InstCbLambda &cbk);\n  QBDI_EXPORT uint32_t addMemAddrCB(rword address, MemoryAccessType type,\n                                    InstCbLambda &&cbk);\n\n  /*! Add a virtual callback which is triggered for any memory access in a\n   * specific address range matching the access type. Virtual callbacks are\n   * called via callback forwarding by a gate callback triggered on every memory\n   * access. This incurs a high performance cost. The callback has the default\n   * priority.\n   *\n   * @param[in] start    Start of the address range which will trigger the\n   *                     callback.\n   * @param[in] end      End of the address range which will trigger the\n   *                     callback.\n   * @param[in] type     A mode bitfield: either QBDI::MEMORY_READ,\n   *                     QBDI::MEMORY_WRITE or both (QBDI::MEMORY_READ_WRITE).\n   * @param[in] cbk      A function pointer to the callback.\n   * @param[in] data     User defined data passed to the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMemRangeCB(rword start, rword end,\n                                     MemoryAccessType type, InstCallback cbk,\n                                     void *data);\n\n  /*! Add a virtual callback which is triggered for any memory access in a\n   * specific address range matching the access type. Virtual callbacks are\n   * called via callback forwarding by a gate callback triggered on every memory\n   * access. This incurs a high performance cost. The callback has the default\n   * priority.\n   *\n   * @param[in] start    Start of the address range which will trigger the\n   *                     callback.\n   * @param[in] end      End of the address range which will trigger the\n   *                     callback.\n   * @param[in] type     A mode bitfield: either QBDI::MEMORY_READ,\n   *                     QBDI::MEMORY_WRITE or both (QBDI::MEMORY_READ_WRITE).\n   * @param[in] cbk      A lambda function to the callback\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addMemRangeCB(rword start, rword end,\n                                     MemoryAccessType type,\n                                     const InstCbLambda &cbk);\n  QBDI_EXPORT uint32_t addMemRangeCB(rword start, rword end,\n                                     MemoryAccessType type, InstCbLambda &&cbk);\n\n  /*! Register a callback event for a specific VM event.\n   *\n   * @param[in] mask  A mask of VM event type which will trigger the callback.\n   * @param[in] cbk   A function pointer to the callback.\n   * @param[in] data  User defined data passed to the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addVMEventCB(VMEvent mask, VMCallback cbk, void *data);\n\n  /*! Register a callback event for a specific VM event.\n   *\n   * @param[in] mask  A mask of VM event type which will trigger the callback.\n   * @param[in] cbk   A lambda function to the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  QBDI_EXPORT uint32_t addVMEventCB(VMEvent mask, const VMCbLambda &cbk);\n  QBDI_EXPORT uint32_t addVMEventCB(VMEvent mask, VMCbLambda &&cbk);\n\n  /*! Remove an instrumentation.\n   *\n   * @param[in] id The id of the instrumentation to remove.\n   *\n   * @return  True if instrumentation has been removed.\n   */\n  QBDI_EXPORT bool deleteInstrumentation(uint32_t id);\n\n  /*! Remove all the registered instrumentations.\n   *\n   */\n  QBDI_EXPORT void deleteAllInstrumentations();\n\n  /*! Obtain the analysis of the current instruction. Analysis results are\n   * cached in the VM. The validity of the returned pointer is only guaranteed\n   * until the end of the callback, else a deepcopy of the structure is\n   * required. This method must only be used in an InstCallback.\n   *\n   * @param[in] [type] Properties to retrieve during analysis.\n   *                   This argument is optional, defaulting to\n   *                   QBDI::ANALYSIS_INSTRUCTION | QBDI::ANALYSIS_DISASSEMBLY.\n   *\n   * @return A InstAnalysis structure containing the analysis result.\n   */\n  QBDI_EXPORT const InstAnalysis *getInstAnalysis(\n      AnalysisType type = ANALYSIS_INSTRUCTION | ANALYSIS_DISASSEMBLY) const;\n\n  /*! Obtain the analysis of a cached instruction. Analysis results are cached\n   * in the VM. The validity of the returned pointer is only guaranteed until\n   * the end of the callback or a call to a noconst method of the VM object.\n   *\n   * @param[in] address The address of the instruction to analyse.\n   * @param[in] [type]  Properties to retrieve during analysis.\n   *                    This argument is optional, defaulting to\n   *                    QBDI::ANALYSIS_INSTRUCTION | QBDI::ANALYSIS_DISASSEMBLY\n   *\n   * @return A InstAnalysis structure containing the analysis result.\n   *    null if the instruction isn't in the cache.\n   */\n  QBDI_EXPORT const InstAnalysis *getCachedInstAnalysis(\n      rword address,\n      AnalysisType type = ANALYSIS_INSTRUCTION | ANALYSIS_DISASSEMBLY) const;\n\n  /*! Obtain the analysis of a JITed instruction. Analysis results are cached\n   * in the VM. The validity of the returned pointer is only guaranteed until\n   * the end of the callback or a call to a noconst method of the VM object.\n   * This API may be used to determine if a given address of the current\n   * process memory correspond to the JIT patch from this VM. Note that this\n   * call may allocate memory.\n   *\n   * @param[in] address The JIT address\n   * @param[in] [type]  Properties to retrieve during analysis.\n   *                    This argument is optional, defaulting to\n   *                    QBDI::ANALYSIS_INSTRUCTION | QBDI::ANALYSIS_DISASSEMBLY\n   *                    | ANALYSIS_JIT\n   *\n   * @return A InstAnalysis structure containing the analysis result.\n   *    null if the address isn't a valid\n   */\n  QBDI_EXPORT const InstAnalysis *\n  getJITInstAnalysis(rword address, AnalysisType type = ANALYSIS_INSTRUCTION |\n                                                        ANALYSIS_DISASSEMBLY |\n                                                        ANALYSIS_JIT) const;\n\n  /*! Add instrumentation rules to log memory access using inline\n   * instrumentation and instruction shadows.\n   *\n   * @param[in] type Memory mode bitfield to activate the logging for:\n   *            either QBDI::MEMORY_READ, QBDI::MEMORY_WRITE or both\n   *            (QBDI::MEMORY_READ_WRITE).\n   *\n   * @return True if inline memory logging is supported, False if not or in case\n   *         of error.\n   */\n  QBDI_EXPORT bool recordMemoryAccess(MemoryAccessType type);\n\n  /*! Obtain the memory accesses made by the last executed instruction.\n   *  The method should be called in an InstCallback.\n   *\n   * @return List of memory access made by the instruction.\n   */\n  QBDI_EXPORT std::vector<MemoryAccess> getInstMemoryAccess() const;\n\n  /*! Obtain the memory accesses made by the last executed basic block.\n   *  The method should be called in a VMCallback with VMEvent::SEQUENCE_EXIT.\n   *\n   * @return List of memory access made by the instruction.\n   */\n  QBDI_EXPORT std::vector<MemoryAccess> getBBMemoryAccess() const;\n\n  /*! Pre-cache a known basic block\n   *  This method mustn't be called if the VM already runs.\n   *\n   * @param[in] pc   Start address of a basic block\n   *\n   * @return True if basic block has been inserted in cache.\n   */\n  QBDI_EXPORT bool precacheBasicBlock(rword pc);\n\n  /*! Clear a specific address range from the translation cache.\n   *\n   * @param[in] start Start of the address range to clear from the cache.\n   * @param[in] end   End of the address range to clear from the cache.\n   *\n   */\n  QBDI_EXPORT void clearCache(rword start, rword end);\n\n  /*! Clear the entire translation cache.\n   */\n  QBDI_EXPORT void clearAllCache();\n\n  /*! Get the number of ExecBlock in the cache. Each block uses 2 memory pages\n   * and some heap allocations.\n   *\n   * @return  The number of ExecBlock in the cache.\n   */\n  QBDI_EXPORT uint32_t getNbExecBlock() const;\n\n  /*! Reduce the cache to X ExecBlock. Note that this will try to purge the\n   * oldest ExecBlock first, but the block may be recreate if needed by followed\n   * execution.\n   *\n   * @param[in] nb The number of BasicBlock that should remains in the cache\n   *               after call.\n   */\n  QBDI_EXPORT void reduceCacheTo(uint32_t nb);\n};\n\n} // namespace QBDI\n\n#endif // QBDI_VM_H_\n"
  },
  {
    "path": "include/QBDI/VM_C.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_VM_C_H_\n#define QBDI_VM_C_H_\n\n#include <stdarg.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/Errors.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/State.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\nextern \"C\" {\n#endif\n\n/*! Create and initialize a VM instance.\n *\n * @param[out] instance VM instance created.\n * @param[in]  cpu      A C string naming the CPU model to use.\n *                      If NULL, the default architecture CPU model is used\n *                      (see LLVM documentation for more details).\n * @param[in]  mattrs   A NULL terminated array of C strings specifying the\n *                      attributes of the cpu model.\n *                      If NULL, no additional features are specified.\n * @param[in]  opts     The options to enable in the VM\n */\nQBDI_EXPORT void qbdi_initVM(VMInstanceRef *instance, const char *cpu,\n                             const char **mattrs, Options opts);\n\n/*! Destroy an instance of VM.\n *  This method mustn't be called when the VM runs.\n *\n * @param[in] instance VM instance.\n */\nQBDI_EXPORT void qbdi_terminateVM(VMInstanceRef instance);\n\n/*! Add an address range to the set of instrumented address ranges.\n *\n * @param[in] instance VM instance.\n * @param[in] start  Start address of the range (included).\n * @param[in] end    End address of the range (excluded).\n */\nQBDI_EXPORT void qbdi_addInstrumentedRange(VMInstanceRef instance, rword start,\n                                           rword end);\n\n/*! Add the executable address ranges of a module to the set of instrumented\n * address ranges.\n *\n * @param[in] instance VM instance.\n * @param[in] name  The module's name.\n *\n * @return  True if at least one range was added to the instrumented ranges.\n */\nQBDI_EXPORT bool qbdi_addInstrumentedModule(VMInstanceRef instance,\n                                            const char *name);\n\n/*! Add the executable address ranges of a module to the set of instrumented\n * address ranges using an address belonging to the module.\n *\n * @param[in] instance  VM instance.\n * @param[in] addr      An address contained by module's range.\n *\n * @return  True if at least one range was added to the instrumented ranges.\n */\nQBDI_EXPORT bool qbdi_addInstrumentedModuleFromAddr(VMInstanceRef instance,\n                                                    rword addr);\n\n/*! Adds all the executable memory maps to the instrumented range set.\n *\n * @param[in] instance VM instance.\n *\n * @return  True if at least one range was added to the instrumented ranges.\n */\nQBDI_EXPORT bool qbdi_instrumentAllExecutableMaps(VMInstanceRef instance);\n\n/*! Remove an address range from the set of instrumented address ranges.\n *\n * @param[in] instance  VM instance.\n * @param[in] start     Start address of the range (included).\n * @param[in] end       End address of the range (excluded).\n */\nQBDI_EXPORT void qbdi_removeInstrumentedRange(VMInstanceRef instance,\n                                              rword start, rword end);\n\n/*! Remove the executable address ranges of a module from the set of\n * instrumented address ranges.\n *\n * @param[in] instance  VM instance.\n * @param[in] name      The module's name.\n *\n * @return  True if at least one range was removed from the instrumented ranges.\n */\nQBDI_EXPORT bool qbdi_removeInstrumentedModule(VMInstanceRef instance,\n                                               const char *name);\n\n/*! Remove the executable address ranges of a module from the set of\n * instrumented address ranges using an address belonging to the module.\n *\n * @param[in] instance  VM instance.\n * @param[in] addr      An address contained by module's range.\n *\n * @return  True if at least one range was removed from the instrumented ranges.\n */\nQBDI_EXPORT bool qbdi_removeInstrumentedModuleFromAddr(VMInstanceRef instance,\n                                                       rword addr);\n\n/*! Remove all instrumented ranges.\n *\n * @param[in] instance  VM instance.\n */\nQBDI_EXPORT void qbdi_removeAllInstrumentedRanges(VMInstanceRef instance);\n\n/*! Start the execution by the DBI from a given address (and stop when another\n * is reached). This method mustn't be called when the VM already runs.\n *\n * @param[in] instance  VM instance.\n * @param[in] start     Address of the first instruction to execute.\n * @param[in] stop      Stop the execution when this instruction is reached.\n *\n * @return  True if at least one block has been executed.\n */\nQBDI_EXPORT bool qbdi_run(VMInstanceRef instance, rword start, rword stop);\n\n/*! Call a function using the DBI (and its current state).\n *  This method mustn't be called when the VM already runs.\n *\n * @param[in] instance   VM instance.\n * @param[in] [retval]   Pointer to the returned value (optional).\n * @param[in] function   Address of the function start instruction.\n * @param[in] argNum     The number of arguments in the variadic list.\n * @param[in] ...        A variadic list of arguments.\n *\n * @return  True if at least one block has been executed.\n *\n * @details Example:\n *\n *     // perform (with QBDI) a call similar to (*funcPtr)(42);\n *     uint8_t *fakestack = NULL;\n *     VMInstanceRef vm;\n *     qbdi_initVM(&vm, NULL, NULL);\n *     GPRState* gprState = qbdi_getGPRState(vm);\n *     qbdi_allocateVirtualStack(gprState, 0x1000000, &fakestack);\n *     qbdi_addInstrumentedModuleFromAddr(vm, funcPtr);\n *     rword retVal;\n *     qbdi_call(vm, &retVal, funcPtr, 1, 42);\n *     qbdi_alignedFree(fakestack);\n *\n */\nQBDI_EXPORT bool qbdi_call(VMInstanceRef instance, rword *retval,\n                           rword function, uint32_t argNum, ...);\n\n/*! Call a function using the DBI (and its current state).\n *  This method mustn't be called when the VM already runs.\n *\n * @param[in] instance   VM instance.\n * @param[in] [retval]   Pointer to the returned value (optional).\n * @param[in] function   Address of the function start instruction.\n * @param[in] argNum     The number of arguments in the variadic list.\n * @param[in] ap         An stdarg va_list object.\n *\n * @return  True if at least one block has been executed.\n */\nQBDI_EXPORT bool qbdi_callV(VMInstanceRef instance, rword *retval,\n                            rword function, uint32_t argNum, va_list ap);\n\n/*! Call a function using the DBI (and its current state).\n *  This method mustn't be called when the VM already runs.\n *\n * @param[in] instance   VM instance.\n * @param[in] [retval]   Pointer to the returned value (optional).\n * @param[in] function   Address of the function start instruction.\n * @param[in] argNum     The number of arguments in the variadic list.\n * @param[in] args       Arguments as an array of rword values.\n *\n * @return  True if at least one block has been executed.\n */\nQBDI_EXPORT bool qbdi_callA(VMInstanceRef instance, rword *retval,\n                            rword function, uint32_t argNum, const rword *args);\n\n/*! Switch the stack and call a function using the DBI (and its current\n *  state).\n *  This method will allocate a new stack and switch to this stack. The\n *  remaining space on the current stack will be use by the called method.\n *  This method mustn't be called if the VM already runs.\n *  The stack pointer in the state must'nt be used after the end of this\n *  method.\n *\n * @param[in] instance   VM instance.\n * @param[in] [retval]   Pointer to the returned value (optional).\n * @param[in] function   Address of the function start instruction.\n * @param[in] stackSize  The size of the stack for the engine.\n * @param[in] argNum     The number of arguments in the variadic list.\n * @param[in] ...        A variadic list of arguments.\n *\n * @return  True if at least one block has been executed.\n *\n * @details Example:\n *\n *     // perform (with QBDI) a call similar to (*funcPtr)(42);\n *     VMInstanceRef vm;\n *     qbdi_initVM(&vm, NULL, NULL);\n *     GPRState* gprState = qbdi_getGPRState(vm);\n *     qbdi_addInstrumentedModuleFromAddr(vm, funcPtr);\n *     rword retVal;\n *     qbdi_switchStackAndCall(vm, &retVal, funcPtr, 0x20000, 1, 42);\n *\n */\nQBDI_EXPORT bool qbdi_switchStackAndCall(VMInstanceRef instance, rword *retval,\n                                         rword function, uint32_t stackSize,\n                                         uint32_t argNum, ...);\n\n/*! Switch the stack and call a function using the DBI (and its current\n *  state).\n *  This method mustn't be called if the VM already runs.\n *  The stack pointer in the state must'nt be used after the end of this\n *  method.\n *\n * @param[in] instance   VM instance.\n * @param[in] [retval]   Pointer to the returned value (optional).\n * @param[in] function   Address of the function start instruction.\n * @param[in] stackSize  The size of the stack for the engine.\n * @param[in] argNum     The number of arguments in the variadic list.\n * @param[in] ap         An stdarg va_list object.\n *\n * @return  True if at least one block has been executed.\n */\nQBDI_EXPORT bool qbdi_switchStackAndCallV(VMInstanceRef instance, rword *retval,\n                                          rword function, uint32_t stackSize,\n                                          uint32_t argNum, va_list ap);\n\n/*! Switch the stack and call a function using the DBI (and its current\n *  state).\n *  This method mustn't be called if the VM already runs.\n *  The stack pointer in the state must'nt be used after the end of this\n *  method.\n *\n * @param[in] instance   VM instance.\n * @param[in] [retval]   Pointer to the returned value (optional).\n * @param[in] function   Address of the function start instruction.\n * @param[in] stackSize  The size of the stack for the engine.\n * @param[in] argNum     The number of arguments in the variadic list.\n * @param[in] args       Arguments as an array of rword values.\n *\n * @return  True if at least one block has been executed.\n */\nQBDI_EXPORT bool qbdi_switchStackAndCallA(VMInstanceRef instance, rword *retval,\n                                          rword function, uint32_t stackSize,\n                                          uint32_t argNum, const rword *args);\n\n/*! Obtain the current general purpose register state.\n *\n * @param[in] instance  VM instance.\n *\n * @return  A structure containing the General Purpose Registers state.\n */\nQBDI_EXPORT GPRState *qbdi_getGPRState(VMInstanceRef instance);\n\n/*! Obtain the current floating point register state.\n *\n * @param[in] instance  VM instance.\n *\n * @return  A structure containing the Floating Point Registers state.\n */\nQBDI_EXPORT FPRState *qbdi_getFPRState(VMInstanceRef instance);\n\n/*! Obtain the backuped value of errno, if the option\n * OPT_DISABLE_ERRNO_BACKUP is not enable.\n *\n * @param[in] instance  VM instance.\n *\n * @return the backupped value of errno\n */\nQBDI_EXPORT uint32_t qbdi_getErrno(VMInstanceRef instance);\n\n/*! Set the GPR state\n *\n * @param[in] instance  VM instance.\n * @param[in] gprState  A structure containing the General Purpose Registers\n *                      state.\n */\nQBDI_EXPORT void qbdi_setGPRState(VMInstanceRef instance, GPRState *gprState);\n\n/*! Set the FPR state\n *\n * @param[in] instance  VM instance.\n * @param[in] fprState  A structure containing the Floating Point Registers\n *                      state.\n */\nQBDI_EXPORT void qbdi_setFPRState(VMInstanceRef instance, FPRState *fprState);\n\n/*! Set the backuped value of errno, if the option\n * OPT_DISABLE_ERRNO_BACKUP is not enable.\n *\n * @param[in] backupErrno the value to set\n */\nQBDI_EXPORT void qbdi_setErrno(VMInstanceRef instance, uint32_t backupErrno);\n\n/*! Get the current Options\n *\n * @param[in] instance  VM instance.\n *\n * @return              The current options of the VM\n */\nQBDI_EXPORT Options qbdi_getOptions(VMInstanceRef instance);\n\n/*! Set the Options\n *  This method mustn't be called when the VM runs.\n *\n * @param[in] instance  VM instance.\n * @param[in] options   The new options of the VM.\n */\nQBDI_EXPORT void qbdi_setOptions(VMInstanceRef instance, Options options);\n\n/*! Add a custom instrumentation rule to the VM.\n *\n * @param[in] instance   VM instance.\n * @param[in] cbk       A function pointer to the callback\n * @param[in] type      Analyse type needed for this instruction function\n *                      pointer to the callback\n * @param[in] data      User defined data passed to the callback.\n *\n * @return The id of the registered instrumentation (or VMError::INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addInstrRule(VMInstanceRef instance,\n                                       InstrRuleCallbackC cbk,\n                                       AnalysisType type, void *data);\n\n/*! Add a custom instrumentation rule to the VM for a range of address\n *\n * @param[in] instance  VM instance.\n * @param[in] start     Begin of the range of address where apply the rule\n * @param[in] end       End of the range of address where apply the rule\n * @param[in] cbk       A function pointer to the callback\n * @param[in] type      Analyse type needed for this instruction function\n *                      pointer to the callback\n * @param[in] data      User defined data passed to the callback.\n *\n * @return The id of the registered instrumentation (or VMError::INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addInstrRuleRange(VMInstanceRef instance, rword start,\n                                            rword end, InstrRuleCallbackC cbk,\n                                            AnalysisType type, void *data);\n\n/*! Add a callback for the current instruction\n *\n * @param[in] cbks      InstrRuleDataVec given in argument\n * @param[in] position  Relative position of the callback\n *                      (QBDI_PREINST / QBDI_POSTINST).\n * @param[in] cbk       A function pointer to the callback\n * @param[in] data      User defined data passed to the callback.\n * @param[in] priority  Priority of the callback\n */\nQBDI_EXPORT void qbdi_addInstrRuleData(InstrRuleDataVec cbks,\n                                       InstPosition position, InstCallback cbk,\n                                       void *data, int priority);\n\n/*! Register a callback event for every memory access matching the type bitfield\n * made by the instructions.\n *\n * @param[in] instance   VM instance.\n * @param[in] type       A mode bitfield: either QBDI_MEMORY_READ,\n *                       QBDI_MEMORY_WRITE or both (QBDI_MEMORY_READ_WRITE).\n * @param[in] cbk        A function pointer to the callback.\n * @param[in] data       User defined data passed to the callback.\n * @param[in] priority   The priority of the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addMemAccessCB(VMInstanceRef instance,\n                                         MemoryAccessType type,\n                                         InstCallback cbk, void *data,\n                                         int priority);\n\n/*! Add a virtual callback which is triggered for any memory access at a\n * specific address matching the access type. Virtual callbacks are called via\n * callback forwarding by a gate callback triggered on every memory access. This\n * incurs a high performance cost.\n *\n * @param[in] instance  VM instance.\n * @param[in] address  Code address which will trigger the callback.\n * @param[in] type     A mode bitfield: either QBDI_MEMORY_READ,\n *                     QBDI_MEMORY_WRITE or both (QBDI_MEMORY_READ_WRITE).\n * @param[in] cbk      A function pointer to the callback.\n * @param[in] data     User defined data passed to the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addMemAddrCB(VMInstanceRef instance, rword address,\n                                       MemoryAccessType type, InstCallback cbk,\n                                       void *data);\n\n/*! Add a virtual callback which is triggered for any memory access in a\n * specific address range matching the access type. Virtual callbacks are called\n * via callback forwarding by a gate callback triggered on every memory access.\n * This incurs a high performance cost.\n *\n * @param[in] instance  VM instance.\n * @param[in] start    Start of the address range which will trigger the\n *                     callback.\n * @param[in] end      End of the address range which will trigger the callback.\n * @param[in] type     A mode bitfield: either QBDI_MEMORY_READ,\n *                     QBDI_MEMORY_WRITE or both (QBDI_MEMORY_READ_WRITE).\n * @param[in] cbk      A function pointer to the callback.\n * @param[in] data     User defined data passed to the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addMemRangeCB(VMInstanceRef instance, rword start,\n                                        rword end, MemoryAccessType type,\n                                        InstCallback cbk, void *data);\n\n/*! Register a callback event if the instruction matches the mnemonic.\n *\n * @param[in] instance   VM instance.\n * @param[in] mnemonic   Mnemonic to match.\n * @param[in] pos        Relative position of the event callback\n *                       (QBDI_PREINST / QBDI_POSTINST).\n * @param[in] cbk        A function pointer to the callback.\n * @param[in] data       User defined data passed to the callback.\n * @param[in] priority   The priority of the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addMnemonicCB(VMInstanceRef instance,\n                                        const char *mnemonic, InstPosition pos,\n                                        InstCallback cbk, void *data,\n                                        int priority);\n\n/*! Register a callback event for a specific instruction event.\n *\n * @param[in] instance  VM instance.\n * @param[in] pos       Relative position of the event callback\n *                      (QBDI_PREINST / QBDI_POSTINST).\n * @param[in] cbk       A function pointer to the callback.\n * @param[in] data      User defined data passed to the callback.\n * @param[in] priority  The priority of the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addCodeCB(VMInstanceRef instance, InstPosition pos,\n                                    InstCallback cbk, void *data, int priority);\n\n/*! Register a callback for when a specific address is executed.\n *\n * @param[in] instance  VM instance.\n * @param[in] address   Code address which will trigger the callback.\n * @param[in] pos       Relative position of the callback\n *                      (QBDI_PREINST / QBDI_POSTINST).\n * @param[in] cbk       A function pointer to the callback.\n * @param[in] data      User defined data passed to the callback.\n * @param[in] priority  The priority of the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addCodeAddrCB(VMInstanceRef instance, rword address,\n                                        InstPosition pos, InstCallback cbk,\n                                        void *data, int priority);\n\n/*! Register a callback for when a specific address range is executed.\n *\n * @param[in] instance  VM instance.\n * @param[in] start  Start of the address range which will trigger the callback.\n * @param[in] end    End of the address range which will trigger the callback.\n * @param[in] pos    Relative position of the callback\n *                   (QBDI_PREINST / QBDI_POSTINST).\n * @param[in] cbk       A function pointer to the callback.\n * @param[in] data      User defined data passed to the callback.\n * @param[in] priority  The priority of the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addCodeRangeCB(VMInstanceRef instance, rword start,\n                                         rword end, InstPosition pos,\n                                         InstCallback cbk, void *data,\n                                         int priority);\n\n/*! Register a callback event for a specific VM event.\n *\n * @param[in] instance  VM instance.\n * @param[in] mask      A mask of VM event type which will trigger the callback.\n * @param[in] cbk       A function pointer to the callback.\n * @param[in] data      User defined data passed to the callback.\n *\n * @return The id of the registered instrumentation (or QBDI_INVALID_EVENTID\n * in case of failure).\n */\nQBDI_EXPORT uint32_t qbdi_addVMEventCB(VMInstanceRef instance, VMEvent mask,\n                                       VMCallback cbk, void *data);\n\n/*! Remove an instrumentation.\n *\n * @param[in] instance  VM instance.\n * @param[in] id        The id of the instrumentation to remove.\n *\n * @return  True if instrumentation has been removed.\n */\nQBDI_EXPORT bool qbdi_deleteInstrumentation(VMInstanceRef instance,\n                                            uint32_t id);\n\n/*! Remove all the registered instrumentations.\n *\n * @param[in] instance  VM instance.\n */\nQBDI_EXPORT void qbdi_deleteAllInstrumentations(VMInstanceRef instance);\n\n/*! Obtain the analysis of the current instruction. Analysis results are cached\n * in the VM. The validity of the returned pointer is only guaranteed until the\n * end of the callback, else a deepcopy of the structure is required. This\n * method must only be used in an InstCallback.\n *\n * @param[in] instance     VM instance.\n * @param[in] type         Properties to retrieve during analysis.\n *\n * @return A InstAnalysis structure containing the analysis result.\n */\nQBDI_EXPORT const InstAnalysis *\nqbdi_getInstAnalysis(const VMInstanceRef instance, AnalysisType type);\n\n/*! Obtain the analysis of a cached instruction. Analysis results are cached in\n * the VM. The validity of the returned pointer is only guaranteed until the end\n * of the callback or a call to a noconst method of the VM instance.\n *\n * @param[in] instance     VM instance.\n * @param[in] address      The address of the instruction to analyse.\n * @param[in] type         Properties to retrieve during analysis.\n *\n * @return A InstAnalysis structure containing the analysis result.\n *    null if the instruction isn't in the cache.\n */\nQBDI_EXPORT const InstAnalysis *\nqbdi_getCachedInstAnalysis(const VMInstanceRef instance, rword address,\n                           AnalysisType type);\n\n/*! Obtain the analysis of a JITed instruction. Analysis results are cached\n * in the VM. The validity of the returned pointer is only guaranteed until\n * the end of the callback or a call to a noconst method of the VM object.\n * This API may be used to determine if a given address of the current\n * process memory correspond to the JIT patch from this VM. Note that this\n * call may allocate memory.\n *\n * @param[in] instance     VM instance.\n * @param[in] address      The JIT address\n * @param[in] type         Properties to retrieve during analysis.\n *\n * @return A InstAnalysis structure containing the analysis result.\n *    null if the address isn't a valid\n */\nQBDI_EXPORT const InstAnalysis *\nqbdi_getJITInstAnalysis(const VMInstanceRef instance, rword address,\n                        AnalysisType type);\n\n/*! Add instrumentation rules to log memory access using inline instrumentation\n and\n *  instruction shadows.\n\n * @param[in] instance  VM instance.\n * @param[in] type      Memory mode bitfield to activate the logging for:\n *                      either QBDI_MEMORY_READ, QBDI_MEMORY_WRITE\n *                      or both (QBDI_MEMORY_READ_WRITE).\n *\n * @return True if inline memory logging is supported, False if not or in case\n of error.\n */\nQBDI_EXPORT bool qbdi_recordMemoryAccess(VMInstanceRef instance,\n                                         MemoryAccessType type);\n\n/*! Obtain the memory accesses made by the last executed instruction.\n *  The method should be called in an InstCallback.\n *  Return NULL and a size of 0 if the instruction made no memory access.\n *\n *  @param[in]  instance     VM instance.\n *  @param[out] size         Will be set to the number of elements in the\n *                           returned array.\n *\n * @return An array of memory accesses made by the instruction.\n */\nQBDI_EXPORT MemoryAccess *qbdi_getInstMemoryAccess(VMInstanceRef instance,\n                                                   size_t *size);\n\n/*! Obtain the memory accesses made by the last executed basic block.\n *  The method should be called in a VMCallback with QBDI_SEQUENCE_EXIT.\n *  Return NULL and a size of 0 if the basic block made no memory access.\n *\n *  @param[in]  instance     VM instance.\n *  @param[out] size         Will be set to the number of elements in the\n *                           returned array.\n *\n * @return An array of memory accesses made by the basic block.\n */\nQBDI_EXPORT MemoryAccess *qbdi_getBBMemoryAccess(VMInstanceRef instance,\n                                                 size_t *size);\n\n/*! Pre-cache a known basic block\n *  This method mustn't be called when the VM runs.\n *\n *  @param[in]  instance     VM instance.\n *  @param[in]  pc           Start address of a basic block\n *\n * @return True if basic block has been inserted in cache.\n */\nQBDI_EXPORT bool qbdi_precacheBasicBlock(VMInstanceRef instance, rword pc);\n\n/*! Clear a specific address range from the translation cache.\n *\n * @param[in] instance     VM instance.\n * @param[in] start        Start of the address range to clear from the cache.\n * @param[in] end          End of the address range to clear from the cache.\n *\n */\nQBDI_EXPORT void qbdi_clearCache(VMInstanceRef instance, rword start,\n                                 rword end);\n\n/*! Clear the entire translation cache.\n *\n * @param[in] instance     VM instance.\n */\nQBDI_EXPORT void qbdi_clearAllCache(VMInstanceRef instance);\n\n/*! Get the number of ExecBlock in the cache. Each block uses 2 memory pages\n * and some heap allocations.\n *\n * @param[in] instance     VM instance.\n *\n * @return  The number of ExecBlock in the cache.\n */\nQBDI_EXPORT uint32_t qbdi_getNbExecBlock(const VMInstanceRef instance);\n\n/*! Reduce the cache to X ExecBlock. Note that this will try to purge the\n * oldest ExecBlock first, but the block may be recreate if needed by\n * followed execution.\n *\n * @param[in] instance  VM instance.\n * @param[in] nb        The number of BasicBlock that should remains in the\n *                      cache after call.\n */\nQBDI_EXPORT void qbdi_reduceCacheTo(VMInstanceRef instance, uint32_t nb);\n\n#ifdef __cplusplus\n} // \"C\"\n} // QBDI::\n#endif\n\n#endif // QBDI_VM_C_H_\n"
  },
  {
    "path": "include/QBDI/Version.h.in",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_VERSION_H_\n#define QBDI_VERSION_H_\n\n#include <stdint.h>\n#include \"QBDI/Platform.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\nextern \"C\" {\n#endif\n\n#define QBDI_VERSION ((@QBDI_VERSION_MAJOR@ << 16 ) | \\\n                      (@QBDI_VERSION_MINOR@ << 8 ) | \\\n                      (@QBDI_VERSION_PATCH@ << 0 ))\n#define QBDI_VERSION_STRING \"@QBDI_VERSION_STRING@\"\n\n#define QBDI_VERSION_MAJOR @QBDI_VERSION_MAJOR@\n#define QBDI_VERSION_MINOR @QBDI_VERSION_MINOR@\n#define QBDI_VERSION_PATCH @QBDI_VERSION_PATCH@\n#define QBDI_VERSION_DEV @QBDI_VERSION_DEV@\n\n#define QBDI_ARCHITECTURE_STRING \"@QBDI_ARCH@\"\n#define QBDI_PLATFORM_STRING \"@QBDI_PLATFORM@\"\n\n/*! Return QBDI version.\n *\n * @param[out] version  QBDI version encoded as an unsigned integer (0xMMmmpp).\n * @return  QBDI version as a string (major.minor.patch).\n */\nQBDI_EXPORT const char* qbdi_getVersion(uint32_t* version);\n\n#ifdef __cplusplus\n/*! Return QBDI version.\n *\n * @param[out] version  QBDI version encoded as an unsigned integer (0xMMmmpp).\n * @return  QBDI version as a string (major.minor.patch).\n */\ninline const char* getVersion(uint32_t* version) {\n    return qbdi_getVersion(version);\n}\n\n} // \"C\"\n} // QBDI::\n#endif\n\n#endif // QBDI_VERSION_H_\n"
  },
  {
    "path": "include/QBDI/arch/AARCH64/Options.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_OPTION_AARCH64_H_\n#define QBDI_OPTION_AARCH64_H_\n\n#include <stdint.h>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Platform.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif\n\ntypedef enum {\n  _QBDI_EI(NO_OPT) = 0, /*!< Default value */\n  // general options between 0 and 23\n  _QBDI_EI(OPT_DISABLE_FPR) = 1 << 0,          /*!< Disable all operation on FPU\n                                                * (SSE, AVX, SIMD). May break\n                                                * the execution if the target\n                                                * use the FPU\n                                                */\n  _QBDI_EI(OPT_DISABLE_OPTIONAL_FPR) = 1 << 1, /*!< Disable context switch\n                                                * optimisation when the target\n                                                * execblock doesn't used FPR\n                                                */\n  _QBDI_EI(OPT_DISABLE_MEMORYACCESS_VALUE) = 1 << 2, /*!< Don't load the value\n                                                      * when perform memory\n                                                      * access.\n                                                      */\n  _QBDI_EI(OPT_DISABLE_ERRNO_BACKUP) = 1 << 3, /*!< Don't save and restore errno\n                                                */\n  // architecture specific option between 24 and 31\n  _QBDI_EI(OPT_DISABLE_LOCAL_MONITOR) =\n      1 << 24, /*!< Disable the local monitor for instruction like stxr */\n  _QBDI_EI(OPT_BYPASS_PAUTH) =\n      1 << 25, /*!< Disable pointeur authentication. When set, QBDI will\n                  strip/ignore the authentication bits instead of checking them.\n                */\n  _QBDI_EI(OPT_ENABLE_BTI) = 1 << 26, /*!< Enable BTI on instrumented code */\n} Options;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(Options)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* QBDI_OPTION_AARCH64_H_ */\n"
  },
  {
    "path": "include/QBDI/arch/AARCH64/State.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_STATE_AARCH64_H_\n#define QBDI_STATE_AARCH64_H_\n\n#ifdef __cplusplus\n#include <type_traits>\n#endif\n#include <inttypes.h>\n#include <stdint.h>\n#include \"QBDI/Platform.h\"\n\n// ============================================================================\n// AARCH64 Context\n// ============================================================================\n\n#define PRIRWORD PRIx64\n\n#define QBDI_NUM_FPR 32\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif // __cplusplus\n\n/*! ARM CPU modes.\n */\ntypedef enum { AARCH64 = 0, DEFAULT = 0, COUNT } CPUMode;\n\ntypedef uint64_t rword;\ntypedef int64_t sword;\n\n// SPHINX_AARCH64_FPRSTATE_BEGIN\n/*! AARCH64 Floating Point Register context.\n */\ntypedef struct QBDI_ALIGNED(8) {\n  __uint128_t v0;\n  __uint128_t v1;\n  __uint128_t v2;\n  __uint128_t v3;\n\n  __uint128_t v4;\n  __uint128_t v5;\n  __uint128_t v6;\n  __uint128_t v7;\n\n  __uint128_t v8;\n  __uint128_t v9;\n  __uint128_t v10;\n  __uint128_t v11;\n\n  __uint128_t v12;\n  __uint128_t v13;\n  __uint128_t v14;\n  __uint128_t v15;\n\n  __uint128_t v16;\n  __uint128_t v17;\n  __uint128_t v18;\n  __uint128_t v19;\n\n  __uint128_t v20;\n  __uint128_t v21;\n  __uint128_t v22;\n  __uint128_t v23;\n\n  __uint128_t v24;\n  __uint128_t v25;\n  __uint128_t v26;\n  __uint128_t v27;\n\n  __uint128_t v28;\n  __uint128_t v29;\n  __uint128_t v30;\n  __uint128_t v31;\n\n  rword fpcr;\n  rword fpsr;\n} FPRState;\n// SPHINX_AARCH64_FPRSTATE_END\n\n// SPHINX_AARCH64_GPRSTATE_BEGIN\n/*! AARCH64 General Purpose Register context.\n */\ntypedef struct QBDI_ALIGNED(8) {\n  rword x0;\n  rword x1;\n  rword x2;\n  rword x3;\n  rword x4;\n  rword x5;\n  rword x6;\n  rword x7;\n  rword x8;\n  rword x9;\n  rword x10;\n  rword x11;\n  rword x12;\n  rword x13;\n  rword x14;\n  rword x15;\n  rword x16;\n  rword x17;\n  rword x18;\n  rword x19;\n  rword x20;\n  rword x21;\n  rword x22;\n  rword x23;\n  rword x24;\n  rword x25;\n  rword x26;\n  rword x27;\n  rword x28;\n  rword x29; // FP (x29)\n  rword lr;  // LR (x30)\n\n  rword sp;\n  rword nzcv;\n  rword pc;\n  // ? rword daif; ?\n\n  /* Internal CPU state\n   * Local monitor state for exclusive load/store instruction\n   */\n  struct {\n    rword addr;\n    rword enable; /* 0=>disable, 1=>exclusive state, use a rword to not break\n                     align */\n  } localMonitor;\n\n} GPRState;\n// SPHINX_AARCH64_GPRSTATE_END\n\nstatic const char *const GPR_NAMES[] = {\n    \"X0\",  \"X1\",   \"X2\",  \"X3\",  \"X4\",  \"X5\",  \"X6\",  \"X7\",  \"X8\",  \"X9\",\n    \"X10\", \"X11\",  \"X12\", \"X13\", \"X14\", \"X15\", \"X16\", \"X17\", \"X18\", \"X19\",\n    \"X20\", \"X21\",  \"X22\", \"X23\", \"X24\", \"X25\", \"X26\", \"X27\", \"X28\",\n    \"X29\", // FP\n    \"LR\",\n\n    \"SP\",  \"NZCV\", \"PC\",\n};\n\nstatic const unsigned int NUM_GPR = 32;\nstatic const unsigned int AVAILABLE_GPR = 28;\nstatic const unsigned int REG_RETURN = 0;\nstatic const unsigned int REG_BP = 29;\nstatic const unsigned int REG_LR = 30;\nstatic const unsigned int REG_SP = 31;\nstatic const unsigned int REG_PC = 33;\nstatic const unsigned int REG_FLAG = 32;\n\n#ifdef __cplusplus\n#define QBDI_GPR_GET(state, i) (reinterpret_cast<const QBDI::rword *>(state)[i])\n#define QBDI_GPR_SET(state, i, v) \\\n  (reinterpret_cast<QBDI::rword *>(state)[i] = v)\n#else\n#define QBDI_GPR_GET(state, i) (((rword *)state)[i])\n#define QBDI_GPR_SET(state, i, v) (((rword *)state)[i] = v)\n#endif\n\n#ifdef __cplusplus\n} // namespace QBDI\n#endif // __cplusplus\n\n#endif // QBDI_STATE_AARCH64_H_\n"
  },
  {
    "path": "include/QBDI/arch/ARM/Options.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_OPTION_ARM_H_\n#define QBDI_OPTION_ARM_H_\n\n#include <stdint.h>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Platform.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif\n\ntypedef enum {\n  _QBDI_EI(NO_OPT) = 0, /*!< Default value */\n  // general options between 0 and 23\n  _QBDI_EI(OPT_DISABLE_FPR) = 1 << 0,          /*!< Disable all operation on FPU\n                                                * (SSE, AVX, SIMD). May break\n                                                * the execution if the target\n                                                * use the FPU\n                                                */\n  _QBDI_EI(OPT_DISABLE_OPTIONAL_FPR) = 1 << 1, /*!< Disable context switch\n                                                * optimisation when the target\n                                                * execblock doesn't used FPR\n                                                */\n  _QBDI_EI(OPT_DISABLE_MEMORYACCESS_VALUE) = 1 << 2, /*!< Don't load the value\n                                                      * when perform memory\n                                                      * access.\n                                                      */\n  _QBDI_EI(OPT_DISABLE_ERRNO_BACKUP) = 1 << 3, /*!< Don't save and restore errno\n                                                */\n  // architecture specific option between 24 and 31\n  _QBDI_EI(OPT_DISABLE_LOCAL_MONITOR) =\n      1 << 24, /*!< Disable the local monitor for instruction like strex */\n  _QBDI_EI(OPT_DISABLE_D16_D31) = 1 << 25, /*!< Disable the used of D16-D31\n                                            * register\n                                            */\n  _QBDI_EI(OPT_ARMv4_bit) = 1 << 26,\n  _QBDI_EI(OPT_ARMv4) = 3 << 26,    /*!< Change between ARM and Thumb as\n                                     * an ARMv4 CPU\n                                     */\n  _QBDI_EI(OPT_ARMv5T_6) = 1 << 27, /*!< Change between ARM and Thumb as\n                                     * an ARMv5T or ARMv6 CPU\n                                     */\n  _QBDI_EI(OPT_ARMv7) = 0,          /*!< Change between ARM and Thumb as\n                                     * an ARMv7 CPU (default)\n                                     */\n  _QBDI_EI(OPT_ARM_MASK) = 3 << 26,\n\n} Options;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(Options)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* QBDI_OPTION_ARM_H_ */\n"
  },
  {
    "path": "include/QBDI/arch/ARM/State.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_STATE_ARM_H_\n#define QBDI_STATE_ARM_H_\n\n#ifdef __cplusplus\n#include <type_traits>\n#endif\n#include <inttypes.h>\n#include <stdint.h>\n#include \"QBDI/Platform.h\"\n\n// ============================================================================\n// ARM Context\n// ============================================================================\n\n#define PRIRWORD PRIx32\n\n#define QBDI_NUM_FPR 32\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif // __cplusplus\n\n/*! ARM CPU modes.\n */\ntypedef enum { ARM = 0, DEFAULT = 0, Thumb, COUNT } CPUMode;\n\ntypedef uint32_t rword;\ntypedef int32_t sword;\n\n// SPHINX_ARM_FPRSTATE_BEGIN\n/*! ARM Floating Point Register context.\n */\ntypedef union {\n  float QBDI_ALIGNED(8) s[32];\n  double QBDI_ALIGNED(8) d[QBDI_NUM_FPR];\n  uint8_t QBDI_ALIGNED(8) q[QBDI_NUM_FPR / 2][16];\n} FPRStateVReg;\n\ntypedef struct QBDI_ALIGNED(8) {\n  FPRStateVReg vreg;\n\n  rword fpscr;\n} FPRState;\n// SPHINX_ARM_FPRSTATE_END\n\n// SPHINX_ARM_GPRSTATE_BEGIN\n/*! ARM General Purpose Register context.\n */\ntypedef struct QBDI_ALIGNED(4) {\n  rword r0;\n  rword r1;\n  rword r2;\n  rword r3;\n  rword r4;\n  rword r5;\n  rword r6;\n  rword r7;\n  rword r8;\n  rword r9;\n  rword r10;\n  rword r11;\n  rword r12;\n  rword sp;\n  rword lr;\n  rword pc;\n  rword cpsr;\n\n  /* Internal CPU state\n   * Local monitor state for exclusive load/store instruction\n   */\n  struct {\n    rword addr;\n    rword enable; /* 0=>disable,\n                   * 1=>enable by ldrexb,\n                   * 2=>enable by ldrexh,\n                   * 4=>enable by ldrex,\n                   * 8=>enable by ldrexd\n                   */\n  } localMonitor;\n\n} GPRState;\n// SPHINX_ARM_GPRSTATE_END\n\nstatic const char *const GPR_NAMES[] = {\n    \"R0\", \"R1\",  \"R2\",  \"R3\",  \"R4\", \"R5\", \"R6\", \"R7\",   \"R8\",\n    \"R9\", \"R10\", \"R11\", \"R12\", \"SP\", \"LR\", \"PC\", \"CPSR\",\n};\n\nstatic const unsigned int NUM_GPR = 16;\nstatic const unsigned int AVAILABLE_GPR = 13;\nstatic const unsigned int REG_RETURN = 0;\nstatic const unsigned int REG_BP = 12;\nstatic const unsigned int REG_SP = 13;\nstatic const unsigned int REG_LR = 14;\nstatic const unsigned int REG_PC = 15;\nstatic const unsigned int REG_FLAG = 16;\n\n#ifdef __cplusplus\n#define QBDI_GPR_GET(state, i) (reinterpret_cast<const QBDI::rword *>(state)[i])\n#define QBDI_GPR_SET(state, i, v) \\\n  (reinterpret_cast<QBDI::rword *>(state)[i] = v)\n#else\n#define QBDI_GPR_GET(state, i) (((rword *)state)[i])\n#define QBDI_GPR_SET(state, i, v) (((rword *)state)[i] = v)\n#endif\n\n#ifdef __cplusplus\n} // namespace QBDI\n#endif // __cplusplus\n\n#endif // QBDI_STATE_ARM_H_\n"
  },
  {
    "path": "include/QBDI/arch/X86/Options.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_OPTION_X86_H_\n#define QBDI_OPTION_X86_H_\n\n#include <stdint.h>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Platform.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif\n\ntypedef enum {\n  _QBDI_EI(NO_OPT) = 0, /*!< Default value */\n  // general options between 0 and 23\n  _QBDI_EI(OPT_DISABLE_FPR) = 1 << 0,          /*!< Disable all operation on FPU\n                                                * (SSE, AVX, SIMD). May break\n                                                * the execution if the target\n                                                * use the FPU\n                                                */\n  _QBDI_EI(OPT_DISABLE_OPTIONAL_FPR) = 1 << 1, /*!< Disable context switch\n                                                * optimisation when the target\n                                                * execblock doesn't used FPR\n                                                */\n  _QBDI_EI(OPT_DISABLE_MEMORYACCESS_VALUE) = 1 << 2, /*!< Don't load the value\n                                                      * when perform memory\n                                                      * access.\n                                                      */\n  _QBDI_EI(OPT_DISABLE_ERRNO_BACKUP) = 1 << 3, /*!< Don't save and restore errno\n                                                */\n  // architecture specific option between 24 and 31\n  _QBDI_EI(OPT_ATT_SYNTAX) = 1 << 24, /*!< Used the AT&T syntax for\n                                       * instruction disassembly\n                                       */\n} Options;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(Options)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* QBDI_OPTION_X86_H_ */\n"
  },
  {
    "path": "include/QBDI/arch/X86/State.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_STATE_X86_H_\n#define QBDI_STATE_X86_H_\n\n#include <inttypes.h>\n#include <stdint.h>\n\n#include \"QBDI/Platform.h\"\n\n// ============================================================================\n// X86 Context\n// ============================================================================\n\n#define PRIRWORD PRIx32\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif // __cplusplus\n\n/*! X86 CPU modes.\n */\ntypedef enum { X86 = 0, DEFAULT = 0, COUNT } CPUMode;\n\ntypedef struct {\n  uint16_t invalid : 1;\n  uint16_t denorm  : 1;\n  uint16_t zdiv    : 1;\n  uint16_t ovrfl   : 1;\n  uint16_t undfl   : 1;\n  uint16_t precis  : 1;\n  uint16_t         : 2;\n  uint16_t pc      : 2;\n  uint16_t rc      : 2;\n  uint16_t /*inf*/ : 1;\n  uint16_t         : 3;\n} FPControl;\n\ntypedef struct {\n  uint16_t invalid : 1;\n  uint16_t denorm  : 1;\n  uint16_t zdiv    : 1;\n  uint16_t ovrfl   : 1;\n  uint16_t undfl   : 1;\n  uint16_t precis  : 1;\n  uint16_t stkflt  : 1;\n  uint16_t errsumm : 1;\n  uint16_t c0      : 1;\n  uint16_t c1      : 1;\n  uint16_t c2      : 1;\n  uint16_t tos     : 3;\n  uint16_t c3      : 1;\n  uint16_t busy    : 1;\n} FPStatus;\n\ntypedef struct {\n  char reg[10];\n  char rsrv[6];\n} MMSTReg;\n\ntypedef uint32_t rword;\ntypedef int32_t sword;\n\n/*! X86 Floating Point Register context.\n */ // SPHINX_X86_FPRSTATE_BEGIN\ntypedef struct QBDI_ALIGNED(16) {\n  union {\n    FPControl fcw; /* x87 FPU control word */\n    uint16_t rfcw;\n  };\n  union {\n    FPStatus fsw; /* x87 FPU status word */\n    uint16_t rfsw;\n  };\n  uint8_t ftw;        /* x87 FPU tag word */\n  uint8_t rsrv1;      /* reserved */\n  uint16_t fop;       /* x87 FPU Opcode */\n  uint32_t ip;        /* x87 FPU Instruction Pointer offset */\n  uint16_t cs;        /* x87 FPU Instruction Pointer Selector */\n  uint16_t rsrv2;     /* reserved */\n  uint32_t dp;        /* x87 FPU Instruction Operand(Data) Pointer offset */\n  uint16_t ds;        /* x87 FPU Instruction Operand(Data) Pointer Selector */\n  uint16_t rsrv3;     /* reserved */\n  uint32_t mxcsr;     /* MXCSR Register state */\n  uint32_t mxcsrmask; /* MXCSR mask */\n  MMSTReg stmm0;      /* ST0/MM0   */\n  MMSTReg stmm1;      /* ST1/MM1  */\n  MMSTReg stmm2;      /* ST2/MM2  */\n  MMSTReg stmm3;      /* ST3/MM3  */\n  MMSTReg stmm4;      /* ST4/MM4  */\n  MMSTReg stmm5;      /* ST5/MM5  */\n  MMSTReg stmm6;      /* ST6/MM6  */\n  MMSTReg stmm7;      /* ST7/MM7  */\n  char xmm0[16];      /* XMM 0  */\n  char xmm1[16];      /* XMM 1  */\n  char xmm2[16];      /* XMM 2  */\n  char xmm3[16];      /* XMM 3  */\n  char xmm4[16];      /* XMM 4  */\n  char xmm5[16];      /* XMM 5  */\n  char xmm6[16];      /* XMM 6  */\n  char xmm7[16];      /* XMM 7  */\n  char reserved[14 * 16];\n  char ymm0[16]; /* YMM0[255:128] */\n  char ymm1[16]; /* YMM1[255:128] */\n  char ymm2[16]; /* YMM2[255:128] */\n  char ymm3[16]; /* YMM3[255:128] */\n  char ymm4[16]; /* YMM4[255:128] */\n  char ymm5[16]; /* YMM5[255:128] */\n  char ymm6[16]; /* YMM6[255:128] */\n  char ymm7[16]; /* YMM7[255:128] */\n} FPRState;\n// SPHINX_X86_FPRSTATE_END\ntypedef char __compile_check_01__[sizeof(FPRState) == 640 ? 1 : -1];\n\n/*! X86 General Purpose Register context.\n */ // SPHINX_X86_GPRSTATE_BEGIN\ntypedef struct QBDI_ALIGNED(4) {\n  rword eax;\n  rword ebx;\n  rword ecx;\n  rword edx;\n  rword esi;\n  rword edi;\n  rword ebp;\n  rword esp;\n  rword eip;\n  rword eflags;\n} GPRState;\n// SPHINX_X86_GPRSTATE_END\n\nstatic const char *const GPR_NAMES[] = {\"EAX\", \"EBX\", \"ECX\", \"EDX\", \"ESI\",\n                                        \"EDI\", \"EBP\", \"ESP\", \"EIP\", \"EFLAGS\"};\n\nstatic const unsigned int NUM_GPR = 9;\nstatic const unsigned int AVAILABLE_GPR = 6;\nstatic const unsigned int REG_RETURN = 0;\nstatic const unsigned int REG_BP = 6;\nstatic const unsigned int REG_SP = 7;\nstatic const unsigned int REG_PC = 8;\nstatic const unsigned int REG_FLAG = 9;\n\n#ifdef __cplusplus\n#define QBDI_GPR_GET(state, i) (reinterpret_cast<const QBDI::rword *>(state)[i])\n#define QBDI_GPR_SET(state, i, v) \\\n  (reinterpret_cast<QBDI::rword *>(state)[i] = v)\n#else\n#define QBDI_GPR_GET(state, i) (((rword *)state)[i])\n#define QBDI_GPR_SET(state, i, v) (((rword *)state)[i] = v)\n#endif\n\n#ifdef __cplusplus\n}\n#endif // __cplusplus\n\n#endif // QBDI_STATE_X86_H_\n"
  },
  {
    "path": "include/QBDI/arch/X86_64/Options.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_OPTION_X86_64_H_\n#define QBDI_OPTION_X86_64_H_\n\n#include <stdint.h>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Platform.h\"\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif\n\ntypedef enum {\n  _QBDI_EI(NO_OPT) = 0, /*!< Default value */\n  // general options between 0 and 23\n  _QBDI_EI(OPT_DISABLE_FPR) = 1 << 0,          /*!< Disable all operation on FPU\n                                                * (SSE, AVX, SIMD). May break\n                                                * the execution if the target\n                                                * use the FPU\n                                                */\n  _QBDI_EI(OPT_DISABLE_OPTIONAL_FPR) = 1 << 1, /*!< Disable context switch\n                                                * optimisation when the target\n                                                * execblock doesn't used FPR\n                                                */\n  _QBDI_EI(OPT_DISABLE_MEMORYACCESS_VALUE) = 1 << 2, /*!< Don't load the value\n                                                      * when perform memory\n                                                      * access.\n                                                      */\n  _QBDI_EI(OPT_DISABLE_ERRNO_BACKUP) = 1 << 3, /*!< Don't save and restore errno\n                                                */\n  // architecture specific option between 24 and 31\n  _QBDI_EI(OPT_ATT_SYNTAX) = 1 << 24,   /*!< Used the AT&T syntax for\n                                         * instruction disassembly\n                                         */\n  _QBDI_EI(OPT_ENABLE_FS_GS) = 1 << 25, /*!< Enable Backup/Restore of FS/GS\n                                         * segment. This option uses the\n                                         * instructions (RD|WR)(FS|GS)BASE that\n                                         * must be supported by the operating\n                                         * system */\n} Options;\n\n_QBDI_ENABLE_BITMASK_OPERATORS(Options)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* QBDI_OPTION_X86_64_H_ */\n"
  },
  {
    "path": "include/QBDI/arch/X86_64/State.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_STATE_X86_64_H_\n#define QBDI_STATE_X86_64_H_\n\n#include <inttypes.h>\n#include <stdint.h>\n\n#include \"QBDI/Platform.h\"\n\n// ============================================================================\n// X86_64 Context\n// ============================================================================\n\n#define PRIRWORD PRIx64\n\n#ifdef __cplusplus\nnamespace QBDI {\n#endif // __cplusplus\n\n/*! X86_64 CPU modes.\n */\ntypedef enum { X86_64 = 0, DEFAULT = 0, COUNT } CPUMode;\n\ntypedef struct {\n  uint16_t invalid : 1;\n  uint16_t denorm  : 1;\n  uint16_t zdiv    : 1;\n  uint16_t ovrfl   : 1;\n  uint16_t undfl   : 1;\n  uint16_t precis  : 1;\n  uint16_t         : 2;\n  uint16_t pc      : 2;\n  uint16_t rc      : 2;\n  uint16_t /*inf*/ : 1;\n  uint16_t         : 3;\n} FPControl;\n\ntypedef struct {\n  uint16_t invalid : 1;\n  uint16_t denorm  : 1;\n  uint16_t zdiv    : 1;\n  uint16_t ovrfl   : 1;\n  uint16_t undfl   : 1;\n  uint16_t precis  : 1;\n  uint16_t stkflt  : 1;\n  uint16_t errsumm : 1;\n  uint16_t c0      : 1;\n  uint16_t c1      : 1;\n  uint16_t c2      : 1;\n  uint16_t tos     : 3;\n  uint16_t c3      : 1;\n  uint16_t busy    : 1;\n} FPStatus;\n\ntypedef struct {\n  char reg[10];\n  char rsrv[6];\n} MMSTReg;\n\ntypedef uint64_t rword;\ntypedef int64_t sword;\n\n/*! X86_64 Floating Point Register context.\n */ // SPHINX_X86_64_FPRSTATE_BEGIN\ntypedef struct QBDI_ALIGNED(16) {\n  union {\n    FPControl fcw; /* x87 FPU control word */\n    uint16_t rfcw;\n  };\n  union {\n    FPStatus fsw; /* x87 FPU status word */\n    uint16_t rfsw;\n  };\n  uint8_t ftw;        /* x87 FPU tag word */\n  uint8_t rsrv1;      /* reserved */\n  uint16_t fop;       /* x87 FPU Opcode */\n  uint32_t ip;        /* x87 FPU Instruction Pointer offset */\n  uint16_t cs;        /* x87 FPU Instruction Pointer Selector */\n  uint16_t rsrv2;     /* reserved */\n  uint32_t dp;        /* x87 FPU Instruction Operand(Data) Pointer offset */\n  uint16_t ds;        /* x87 FPU Instruction Operand(Data) Pointer Selector */\n  uint16_t rsrv3;     /* reserved */\n  uint32_t mxcsr;     /* MXCSR Register state */\n  uint32_t mxcsrmask; /* MXCSR mask */\n  MMSTReg stmm0;      /* ST0/MM0   */\n  MMSTReg stmm1;      /* ST1/MM1  */\n  MMSTReg stmm2;      /* ST2/MM2  */\n  MMSTReg stmm3;      /* ST3/MM3  */\n  MMSTReg stmm4;      /* ST4/MM4  */\n  MMSTReg stmm5;      /* ST5/MM5  */\n  MMSTReg stmm6;      /* ST6/MM6  */\n  MMSTReg stmm7;      /* ST7/MM7  */\n  char xmm0[16];      /* XMM 0  */\n  char xmm1[16];      /* XMM 1  */\n  char xmm2[16];      /* XMM 2  */\n  char xmm3[16];      /* XMM 3  */\n  char xmm4[16];      /* XMM 4  */\n  char xmm5[16];      /* XMM 5  */\n  char xmm6[16];      /* XMM 6  */\n  char xmm7[16];      /* XMM 7  */\n  char xmm8[16];      /* XMM 8  */\n  char xmm9[16];      /* XMM 9  */\n  char xmm10[16];     /* XMM 10  */\n  char xmm11[16];     /* XMM 11  */\n  char xmm12[16];     /* XMM 12  */\n  char xmm13[16];     /* XMM 13  */\n  char xmm14[16];     /* XMM 14  */\n  char xmm15[16];     /* XMM 15  */\n  char reserved[6 * 16];\n  char ymm0[16];  /* YMM0[255:128] */\n  char ymm1[16];  /* YMM1[255:128] */\n  char ymm2[16];  /* YMM2[255:128] */\n  char ymm3[16];  /* YMM3[255:128] */\n  char ymm4[16];  /* YMM4[255:128] */\n  char ymm5[16];  /* YMM5[255:128] */\n  char ymm6[16];  /* YMM6[255:128] */\n  char ymm7[16];  /* YMM7[255:128] */\n  char ymm8[16];  /* YMM8[255:128] */\n  char ymm9[16];  /* YMM9[255:128] */\n  char ymm10[16]; /* YMM10[255:128] */\n  char ymm11[16]; /* YMM11[255:128] */\n  char ymm12[16]; /* YMM12[255:128] */\n  char ymm13[16]; /* YMM13[255:128] */\n  char ymm14[16]; /* YMM14[255:128] */\n  char ymm15[16]; /* YMM15[255:128] */\n} FPRState;\n// SPHINX_X86_64_FPRSTATE_END\ntypedef char __compile_check_01__[sizeof(FPRState) == 768 ? 1 : -1];\n\n/*! X86_64 General Purpose Register context.\n */ // SPHINX_X86_64_GPRSTATE_BEGIN\ntypedef struct QBDI_ALIGNED(8) {\n  rword rax;\n  rword rbx;\n  rword rcx;\n  rword rdx;\n  rword rsi;\n  rword rdi;\n  rword r8;\n  rword r9;\n  rword r10;\n  rword r11;\n  rword r12;\n  rword r13;\n  rword r14;\n  rword r15;\n  rword rbp;\n  rword rsp;\n  rword rip;\n  rword eflags;\n  // Only backup and restore with OPT_ENABLE_FS_GS\n  rword fs;\n  rword gs;\n} GPRState;\n// SPHINX_X86_64_GPRSTATE_END\n\nstatic const char *const GPR_NAMES[] = {\n    \"RAX\", \"RBX\", \"RCX\", \"RDX\", \"RSI\", \"RDI\", \"R8\",  \"R9\",     \"R10\", \"R11\",\n    \"R12\", \"R13\", \"R14\", \"R15\", \"RBP\", \"RSP\", \"RIP\", \"EFLAGS\", \"FS\",  \"GS\"};\n\nstatic const unsigned int NUM_GPR = 17;\nstatic const unsigned int AVAILABLE_GPR = 14;\nstatic const unsigned int REG_RETURN = 0;\nstatic const unsigned int REG_BP = 14;\nstatic const unsigned int REG_SP = 15;\nstatic const unsigned int REG_PC = 16;\nstatic const unsigned int REG_FLAG = 17;\n\n#ifdef __cplusplus\n#define QBDI_GPR_GET(state, i) (reinterpret_cast<const QBDI::rword *>(state)[i])\n#define QBDI_GPR_SET(state, i, v) \\\n  (reinterpret_cast<QBDI::rword *>(state)[i] = v)\n#else\n#define QBDI_GPR_GET(state, i) (((rword *)state)[i])\n#define QBDI_GPR_SET(state, i, v) (((rword *)state)[i] = v)\n#endif\n\n#ifdef __cplusplus\n}\n#endif // __cplusplus\n\n#endif // QBDI_STATE_X86_64_H_\n"
  },
  {
    "path": "include/QBDI.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_H_\n#define QBDI_H_\n\n#ifdef __cplusplus\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/VM.h\"\n#else\n#include \"QBDI/Memory.h\"\n#include \"QBDI/VM_C.h\"\n#endif\n\n#include \"QBDI/Logs.h\"\n#include \"QBDI/Version.h\"\n\n#endif // QBDI_H_\n"
  },
  {
    "path": "package/.dockerignore",
    "content": "QBDI-*\n"
  },
  {
    "path": "package/CMakeLists.txt",
    "content": "set(CPACK_PACKAGE_DESCRIPTION_SUMMARY\n    \"QuarkslaB Dynamic binary Instrumentation\")\nset(CPACK_PACKAGE_VENDOR \"Quarkslab\")\nset(CPACK_DEBIAN_PACKAGE_MAINTAINER \"QBDI Team <qbdi@quarslab.com>\")\nset(CPACK_PACKAGE_VERSION_MAJOR \"${QBDI_VERSION_MAJOR}\")\nset(CPACK_PACKAGE_VERSION_MINOR \"${QBDI_VERSION_MINOR}\")\nset(CPACK_PACKAGE_VERSION_PATCH \"${QBDI_VERSION_PATCH}\")\nif(QBDI_PTRAUTH)\n  set(CPACK_SYSTEM_NAME ${QBDI_PLATFORM}-${QBDI_ARCH}-arm64e)\nelse()\n  set(CPACK_SYSTEM_NAME ${QBDI_PLATFORM}-${QBDI_ARCH})\nendif()\nset(CPACK_PACKAGE_FILE_NAME \"QBDI-${QBDI_VERSION_STRING}-${CPACK_SYSTEM_NAME}\")\nif(QBDI_PLATFORM_WINDOWS)\n  if(QBDI_ARCH_X86_64)\n    set(CPACK_GENERATOR \"NSIS64\")\n  else()\n    set(CPACK_GENERATOR \"NSIS\")\n  endif()\n  set(CPACK_NSIS_PACKAGE_NAME \"QBDI ${QBDI_VERSION_STRING} (${QBDI_ARCH})\")\nelseif(QBDI_PLATFORM_MACOS)\n  set(CPACK_GENERATOR \"productbuild\")\nelse()\n  set(CPACK_GENERATOR \"TGZ\")\nendif()\n\nif(FULL_PACKAGE)\n  set(CPACK_SET_DESTDIR true)\n  set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})\n  set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY \"./\")\n  set(CPACK_COMPONENTS_ALL \"QBDI;QBDITemplate;QBDIFridaTemplate\")\nelse()\n  set(CPACK_COMPONENTS_ALL \"QBDI\")\nendif()\nset(CPACK_RESOURCE_FILE_WELCOME \"${CMAKE_CURRENT_SOURCE_DIR}/Welcome.txt\")\nset(CPACK_RESOURCE_FILE_README \"${CMAKE_CURRENT_SOURCE_DIR}/Readme.txt\")\nset(CPACK_RESOURCE_FILE_LICENSE \"${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE.txt\")\n\ninclude(CPack)\n"
  },
  {
    "path": "package/Readme.txt",
    "content": "QuarkslaB Dynamic binary Instrumentation (QBDI) is a custom, tailored, cross platform and cross architecture DBI framework. It aims to support Linux, macOS, Android, iOS and Windows operating systems running on X86, X86_64, ARM and AArch64 architectures.\n\nQBDI is still in development. It provides an engine able to execute arbitrary assembly code, and to insert instrumentation before and after instructions.\n\nThe x86-64 and x86 support is quite good. The ARM and AARCH64 support are still young and may be unstable.\n"
  },
  {
    "path": "package/Welcome.txt",
    "content": "This program will guide you through the installation of QBDI framework.\n"
  },
  {
    "path": "package/launch_docker_packager.sh",
    "content": "#!/usr/bin/env bash\nset -e\nset -x\n\nBASEDIR=$(cd $(dirname \"$0\") && pwd -P)\nGITDIR=$(git rev-parse --show-toplevel)\n\nTARGET_ARCH=\"$1\"\n\n. \"${GITDIR}/docker/common.sh\"\n\nmkdir -p \"${BASEDIR}/package-${QBDI_VERSION}/python\"\n\nbuild_ubuntu_debian() {\n    OS=\"$1\"\n    TAG=\"$2\"\n    TARGET=\"$3\"\n    CMAKE_ARGUMENT=\"$4\"\n    IMG_TAG=\"qbdi:package_${OS}_${TAG}_${TARGET}\"\n    DOCKER_PLATFORM=\"linux/amd64\"\n\n    if [[ -n \"$TARGET_ARCH\" ]] && [[ \"$TARGET\" != \"$TARGET_ARCH\" ]]; then\n        return\n    fi\n\n    if [[ \"$TARGET\" = \"X86\" ]]; then\n        DOCKER_IMG=\"i386/${OS}:${TAG}\"\n        DOCKER_PLATFORM=\"linux/386\"\n    elif [[ \"$TARGET\" = \"ARM\" ]]; then\n        DOCKER_IMG=\"arm32v7/${OS}:${TAG}\"\n        DOCKER_PLATFORM=\"linux/arm/v7\"\n    elif [[ \"$TARGET\" = \"AARCH64\" ]]; then\n        DOCKER_IMG=\"arm64v8/${OS}:${TAG}\"\n        DOCKER_PLATFORM=\"linux/arm64/v8\"\n    else\n        DOCKER_IMG=\"${OS}:${TAG}\"\n    fi\n\n    DOCKERFILE=\"${GITDIR}/docker/ubuntu_debian/Dockerfile\"\n\n    docker build \"${BASEDIR}\" -t ${IMG_TAG} -f \"${DOCKERFILE}\" \\\n                              --platform=\"$DOCKER_PLATFORM\" \\\n                              --build-arg DOCKER_IMG=\"${DOCKER_IMG}\" \\\n                              --build-arg QBDI_ARCH=\"$TARGET\" \\\n                              --build-arg CMAKE_ARGUMENT=\"$CMAKE_ARGUMENT\" \\\n                              --pull \\\n                              --target builder\n\n    # deb is already create, but need to create the tar.gz\n    docker run -w \"$DOCKER_BUILD_DIR\" --name package ${IMG_TAG} cpack\n    docker cp \"package:${DOCKER_BUILD_DIR}/QBDI-${QBDI_VERSION}-linux-${TARGET}.deb\" \"${BASEDIR}/package-${QBDI_VERSION}/QBDI-${QBDI_VERSION}-${OS}${TAG}-${TARGET}.deb\"\n    docker cp \"package:${DOCKER_BUILD_DIR}/QBDI-${QBDI_VERSION}-linux-${TARGET}.tar.gz\" \"${BASEDIR}/package-${QBDI_VERSION}/QBDI-${QBDI_VERSION}-${OS}${TAG}-${TARGET}.tar.gz\"\n    docker rm package\n}\n\nbuild_archlinux () {\n    TARGET=\"$1\"\n    IMG_TAG=\"qbdi:package_archlinux_${TARGET}\"\n\n    if [[ -n \"$TARGET_ARCH\" ]] && [[ \"$TARGET\" != \"$TARGET_ARCH\" ]]; then\n        return\n    fi\n\n    docker build \"${BASEDIR}\" -t ${IMG_TAG} \\\n                              -f \"${GITDIR}/docker/archlinux/Dockerfile.${TARGET}\" \\\n                              --build-arg QBDI_ARCH=\"$TARGET\" \\\n                              --pull\n\n    docker create --name package ${IMG_TAG}\n    docker cp \"package:${DOCKER_BUILD_DIR}/QBDI-${TARGET}-${QBDI_VERSION//-/_}-1-x86_64.pkg.tar.zst\" \"${BASEDIR}/package-${QBDI_VERSION}/QBDI-${QBDI_VERSION}-archlinux$(date +%F)-${TARGET}.pkg.tar.zst\"\n    docker rm package\n}\n\nprepare_archive\n\n# debian13 ARM\nbuild_ubuntu_debian debian \"${DEBIAN_TARGET}\" ARM\n\n# debian13 AARCH64\nbuild_ubuntu_debian debian \"${DEBIAN_TARGET}\" AARCH64\n\n# debian13 x86\nbuild_ubuntu_debian debian \"${DEBIAN_TARGET}\" X86\n\n# debian13 x64\nbuild_ubuntu_debian debian \"${DEBIAN_TARGET}\" X86_64\n\n# ubuntu lts x64\nbuild_ubuntu_debian ubuntu \"${UBUNTU_LTS_TARGET}\" X86_64\n\n# ubuntu lts ARM\nbuild_ubuntu_debian ubuntu \"${UBUNTU_LTS_TARGET}\" ARM\n\n# ubuntu lts AARCH64\nbuild_ubuntu_debian ubuntu \"${UBUNTU_LTS_TARGET}\" AARCH64\n\n# ubuntu 25.10 x64\nbuild_ubuntu_debian ubuntu \"${UBUNTU_LAST_TARGET}\" X86_64\n\n# ubuntu 25.10 ARM\nbuild_ubuntu_debian ubuntu \"${UBUNTU_LAST_TARGET}\" ARM\n\n# ubuntu 25.10 AARCH64\nbuild_ubuntu_debian ubuntu \"${UBUNTU_LAST_TARGET}\" AARCH64\n\n# archlinux x64\nbuild_archlinux X86_64\n\n# archlinux x86\nbuild_archlinux X86\n\n\ndelete_archive\n\n"
  },
  {
    "path": "package/qbdi-frida-template.in",
    "content": "#!/bin/sh\ncp @CMAKE_INSTALL_PREFIX@/@RESOURCES_PREFIX@/qbdi_frida_template/* .\ncp @CMAKE_INSTALL_PREFIX@/@RESOURCES_PREFIX@/frida-qbdi.js .\n\n"
  },
  {
    "path": "package/qbdi-preload-template.in",
    "content": "#!/bin/sh\ncp @CMAKE_INSTALL_PREFIX@/@PRELOAD_RESOURCES_PREFIX@/qbdi_preload_template/* .\n"
  },
  {
    "path": "package/qbdi-template.in",
    "content": "#!/bin/sh\ncp @CMAKE_INSTALL_PREFIX@/@RESOURCES_PREFIX@/qbdi_template/* .\n"
  },
  {
    "path": "package/qbdi.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${exec_prefix}/lib\nincludedir=${prefix}/include\n\nName: qbdi\nDescription: qbdi\nVersion: @QBDI_VERSION_MAJOR@.@QBDI_VERSION_MINOR@\nLibs: -L${libdir} @LLVM_LIBS_STR@ -lQBDI\nCflags: -I${includedir}\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\n  \"setuptools>=61.0\",\n  \"wheel\",\n  \"ninja\",\n  \"cmake>=3.28\",\n]\n\nbuild-backend = \"setuptools.build_meta\"\n"
  },
  {
    "path": "setup.cfg",
    "content": "[metadata]\nname = PyQBDI\nversion = 0.12.2a1\ndescription = Python binding for QBDI\nlong_description = file: README-pypi.rst\nlong_description_content_type = text/x-rst\nlicense = apache2\nlicense_files =\n    LICENSE.txt\nauthor = Nicolas Surbayrole\nauthor_email = qbdi@quarkslab.com\nclassifiers =\n    Development Status :: 5 - Production/Stable\n    Operating System :: Microsoft :: Windows\n    Operating System :: MacOS\n    Operating System :: POSIX :: Linux\n    Programming Language :: C++\n    Programming Language :: Python :: 3.8\n    Programming Language :: Python :: 3.9\n    Programming Language :: Python :: 3.10\n    Programming Language :: Python :: 3.11\n    Programming Language :: Python :: 3.12\n    Programming Language :: Python :: 3.13\n    Programming Language :: Python :: 3.14\n    Topic :: Security\n    Topic :: Software Development :: Debuggers\nproject_urls =\n    Documentation = https://qbdi.readthedocs.io/\n    Source = https://github.com/QBDI/QBDI\n    Homepage = https://qbdi.quarkslab.com/\n\n[options]\nzip_safe = false\npython_requires = >= 3.8\nsetup_requires =\n    setuptools\n    wheel\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python3\n\n# This file is part of pyQBDI (python binding for QBDI).\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport re\nimport sys\nimport platform\nimport subprocess\nimport shutil\ntry:\n    import ninja\n    HAS_NINJA = True\nexcept:\n    HAS_NINJA = False\n\nfrom setuptools import setup, Extension\nfrom setuptools.command.build_ext import build_ext\nfrom packaging.version import Version\n\ndef detect_QBDI_platform():\n    current_os = None\n    arch = None\n    if hasattr(sys.implementation, \"_multiarch\"):\n        if '-' in sys.implementation._multiarch:\n            base_arch, base_os = sys.implementation._multiarch.split('-')[:2]\n        else:\n            base_arch = platform.machine()\n            base_os = sys.implementation._multiarch\n    else:\n        base_arch = platform.machine()\n        base_os = platform.system()\n\n    base_arch = base_arch.lower()\n    base_os = base_os.lower()\n\n    if base_os == 'darwin':\n        current_os = 'macos'\n    elif base_os == 'windows':\n        current_os = 'windows'\n    elif base_os == 'linux':\n        current_os = 'linux'\n\n    if base_arch in ['amd64', 'amd', 'x64', 'x86_64', 'x86', 'i386', 'i686']:\n        # intel arch\n        if sys.maxsize > 2**32:\n            arch = \"X86_64\"\n        else:\n            arch = \"X86\"\n\n    elif base_arch in ['aarch64', 'arm64', 'aarch64_be', 'armv8b', 'armv8l']:\n        assert sys.maxsize > 2**32\n        arch = \"AARCH64\"\n\n    elif base_arch == \"arm\" or \\\n         platform.machine().startswith('armv4') or \\\n         platform.machine().startswith('armv5') or \\\n         platform.machine().startswith('armv6') or \\\n         platform.machine().startswith('armv7'):\n        assert sys.maxsize < 2**32\n        arch = \"ARM\"\n\n    if current_os and arch:\n        return (current_os, arch)\n\n    raise RuntimeError(\"Cannot determine the QBDI platform : system={}, machine={}, is64bits={}\".format(\n                            base_arch, base_os, sys.maxsize > 2**32))\n\n\nclass CMakeExtension(Extension):\n    def __init__(self, name, sourcedir=''):\n        Extension.__init__(self, name, sources=[])\n        self.sourcedir = os.path.abspath(sourcedir)\n\nclass CMakeBuild(build_ext):\n\n    def build_extension(self, ext):\n        extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))\n        detected_platform, detected_arch = detect_QBDI_platform()\n        python_version = Version(platform.python_version()).base_version\n\n        cmake_args = ['-G', 'Ninja',\n                      '-DPYQBDI_OUTPUT_DIRECTORY={}'.format(extdir),\n                      '-DPython_EXECUTABLE={}'.format(os.path.realpath(sys.executable)),\n                      '-DPython3_EXECUTABLE={}'.format(os.path.realpath(sys.executable)),\n                      '-DCMAKE_BUILD_TYPE=Release',\n                      '-DQBDI_PLATFORM={}'.format(detected_platform),\n                      '-DQBDI_ARCH={}'.format(detected_arch),\n                      '-DQBDI_BENCHMARK=OFF',\n                      '-DQBDI_INSTALL=OFF',\n                      '-DQBDI_INCLUDE_DOCS=OFF',\n                      '-DQBDI_INCLUDE_PACKAGE=OFF',\n                      '-DQBDI_SHARED_LIBRARY=OFF',\n                      '-DQBDI_TEST=OFF',\n                      '-DQBDI_TOOLS_FRIDAQBDI=OFF',\n                      '-DQBDI_TOOLS_PYQBDI=ON',\n                      '-DQBDI_TOOLS_PYQBDI_TARGET_PYTHON_VERSION={}'.format(python_version),\n                     ]\n        if os.getenv('CMAKE_OSX_ARCHITECTURES') == 'arm64e':\n            cmake_args += ['-DQBDI_PTRAUTH=ON']\n        build_args = ['--config', 'Release', '--']\n\n        if HAS_NINJA:\n            ninja_executable_path = os.path.abspath(os.path.join(ninja.BIN_DIR,\n                    \"ninja.exe\" if detected_platform == 'windows' else \"ninja\"))\n\n            if not os.path.isfile(ninja_executable_path):\n                raise RuntimeError(\"Compile Error : Cannot found ninja binary.\")\n\n            cmake_args.append('-DCMAKE_MAKE_PROGRAM:FILEPATH={}'.format(ninja_executable_path))\n        elif not bool(shutil.which('ninja')):\n            raise RuntimeError(\"Compile Error : Cannot found ninja binary.\")\n\n        env = os.environ.copy()\n        env['CXXFLAGS'] = '{} -DVERSION_INFO=\\\\\"{}\\\\\"'.format(env.get('CXXFLAGS', ''),\n                                                              self.distribution.get_version())\n        for var in [\"Python3_ROOT_DIR\", \"Python_ROOT_DIR\"]:\n            if var in env:\n                cmake_args.append(\"-D{}={}\".format(var, env[var]))\n            elif var.upper() in env:\n                cmake_args.append(\"-D{}={}\".format(var, env[var.upper()]))\n\n        if not os.path.exists(self.build_temp):\n            os.makedirs(self.build_temp)\n\n        subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env)\n        subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp)\n\n        if not os.path.isdir(extdir):\n            raise RuntimeError(\"Compile Error : No library available.\")\n\nsetup(\n    ext_modules=[CMakeExtension('pyqbdi')],\n    cmdclass={\n        \"build_ext\": CMakeBuild,\n    },\n)\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "include(\"${CMAKE_CURRENT_LIST_DIR}/Engine/CMakeLists.txt\")\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/ExecBlock/CMakeLists.txt\")\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/ExecBroker/CMakeLists.txt\")\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/Patch/CMakeLists.txt\")\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/Utility/CMakeLists.txt\")\n\ntarget_sources(QBDI_shared_src\n               INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/fridaStubs.cpp\")\n\nif(QBDI_PLATFORM_WINDOWS)\n  target_sources(QBDI_shared_src\n                 INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/windowsDllMain.cpp\")\nendif()\n"
  },
  {
    "path": "src/Engine/CMakeLists.txt",
    "content": "# Add QBDI target\nset(SOURCES\n    \"${CMAKE_CURRENT_LIST_DIR}/Engine.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/LLVMCPU.cpp\" \"${CMAKE_CURRENT_LIST_DIR}/VM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/VM_C.cpp\")\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Engine/Engine.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <cstdint>\n#include <string.h>\n\n#include \"llvm/ADT/ArrayRef.h\"\n#include \"llvm/MC/MCInst.h\"\n\n#include \"Engine/Engine.h\"\n#include \"Engine/LLVMCPU.h\"\n\n#include \"ExecBlock/Context.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"ExecBlock/ExecBlockManager.h\"\n#include \"ExecBroker/ExecBroker.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/InstrRule.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchRuleAssembly.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/Errors.h\"\n#include \"QBDI/PtrAuth.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n\n#include \"spdlog/fmt/bin_to_hex.h\"\n\n// Mask to identify VM events\n#define EVENTID_VM_MASK (1UL << 30)\n\nnamespace QBDI {\n\nEngine::Engine(const std::string &_cpu, const std::vector<std::string> &_mattrs,\n               Options opts, VMInstanceRef vminstance)\n    : vminstance(vminstance), instrRulesCounter(0), vmCallbacksCounter(0),\n      curCPUMode(CPUMode::DEFAULT), options(opts), eventMask(VMEvent::NO_EVENT),\n      running(false) {\n\n  llvmCPUs = std::make_unique<LLVMCPUs>(_cpu, _mattrs, opts);\n  blockManager = std::make_unique<ExecBlockManager>(*llvmCPUs, vminstance);\n  execBroker = blockManager->getExecBroker();\n\n  // Get Patch rules Assembly for this architecture\n  patchRuleAssembly = std::make_unique<PatchRuleAssembly>(options);\n\n  gprState = std::make_unique<GPRState>();\n  fprState = std::make_unique<FPRState>();\n  curGPRState = gprState.get();\n  curFPRState = fprState.get();\n\n  initGPRState();\n  initFPRState();\n\n  curExecBlock = nullptr;\n}\n\nEngine::~Engine() = default;\n\nEngine::Engine(const Engine &other)\n    : vminstance(nullptr), instrRules(),\n      instrRulesCounter(other.instrRulesCounter),\n      vmCallbacks(other.vmCallbacks),\n      vmCallbacksCounter(other.vmCallbacksCounter),\n      curCPUMode(CPUMode::DEFAULT), options(other.options),\n      eventMask(other.eventMask), running(false) {\n\n  llvmCPUs = std::make_unique<LLVMCPUs>(\n      other.llvmCPUs->getCPU(), other.llvmCPUs->getMattrs(), other.options);\n  blockManager = std::make_unique<ExecBlockManager>(*llvmCPUs, nullptr);\n  execBroker = blockManager->getExecBroker();\n  // copy instrumentation range\n  execBroker->setInstrumentedRange(other.execBroker->getInstrumentedRange());\n\n  // Get Patch rules Assembly for this architecture\n  patchRuleAssembly = std::make_unique<PatchRuleAssembly>(options);\n\n  // Copy unique_ptr of instrRules\n  for (const auto &r : other.instrRules) {\n    instrRules.emplace_back(r.first, r.second->clone());\n  }\n\n  gprState = std::make_unique<GPRState>();\n  fprState = std::make_unique<FPRState>();\n  curGPRState = gprState.get();\n  curFPRState = fprState.get();\n  setGPRState(other.getGPRState());\n  setFPRState(other.getFPRState());\n\n  curExecBlock = nullptr;\n}\n\nEngine &Engine::operator=(const Engine &other) {\n  QBDI_REQUIRE_ABORT(not running, \"Cannot assign a running Engine\");\n  this->clearAllCache();\n\n  if (not llvmCPUs->isSameCPU(*other.llvmCPUs)) {\n    blockManager.reset();\n\n    llvmCPUs = std::make_unique<LLVMCPUs>(\n        other.llvmCPUs->getCPU(), other.llvmCPUs->getMattrs(), other.options);\n\n    blockManager = std::make_unique<ExecBlockManager>(*llvmCPUs, nullptr);\n    execBroker = blockManager->getExecBroker();\n  }\n\n  this->setOptions(other.options);\n\n  // copy the configuration\n  instrRules.clear();\n  for (const auto &r : other.instrRules) {\n    instrRules.emplace_back(r.first, r.second->clone());\n  }\n  vmCallbacks = other.vmCallbacks;\n  instrRulesCounter = other.instrRulesCounter;\n  vmCallbacksCounter = other.vmCallbacksCounter;\n  eventMask = other.eventMask;\n\n  // copy instrumentation range\n  execBroker->setInstrumentedRange(other.execBroker->getInstrumentedRange());\n\n  // copy state\n  setGPRState(other.getGPRState());\n  setFPRState(other.getFPRState());\n\n  return *this;\n}\n\nvoid Engine::setOptions(Options options) {\n  QBDI_REQUIRE_ABORT(not running, \"Cannot setOptions on a running Engine\");\n  if (options != this->options) {\n    QBDI_DEBUG(\"Change Options from {:x} to {:x}\", this->options, options);\n    clearAllCache();\n    llvmCPUs->setOptions(options);\n\n    // need to recreate all ExecBlock\n    if (patchRuleAssembly->changeOptions(options)) {\n      const RangeSet<rword> instrumentationRange =\n          execBroker->getInstrumentedRange();\n\n      blockManager = std::make_unique<ExecBlockManager>(*llvmCPUs, vminstance);\n      execBroker = blockManager->getExecBroker();\n\n      execBroker->setInstrumentedRange(instrumentationRange);\n    }\n    this->options = options;\n  }\n}\n\nvoid Engine::changeVMInstanceRef(VMInstanceRef vminstance) {\n  QBDI_REQUIRE_ABORT(not running,\n                     \"Cannot changeVMInstanceRef on a running Engine\");\n  this->vminstance = vminstance;\n\n  blockManager->changeVMInstanceRef(vminstance);\n\n  for (auto &r : instrRules) {\n    r.second->changeVMInstanceRef(vminstance);\n  }\n}\n\nvoid Engine::initGPRState() { memset(gprState.get(), 0, sizeof(GPRState)); }\n\nvoid Engine::initFPRState() {\n  memset(fprState.get(), 0, sizeof(FPRState));\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  fprState->rfcw = 0x37F;\n  fprState->ftw = 0x0;\n  fprState->rsrv1 = 0x0;\n  fprState->mxcsr = 0x1F80;\n  fprState->mxcsrmask = 0xFFFF;\n#endif\n}\n\nGPRState *Engine::getGPRState() const { return curGPRState; }\n\nFPRState *Engine::getFPRState() const { return curFPRState; }\n\nvoid Engine::setGPRState(const GPRState *gprState) {\n  QBDI_REQUIRE_ACTION(gprState, return);\n  *(this->curGPRState) = *gprState;\n}\n\nvoid Engine::setFPRState(const FPRState *fprState) {\n  QBDI_REQUIRE_ACTION(fprState, return);\n  *(this->curFPRState) = *fprState;\n}\n\nbool Engine::isPreInst() const {\n  if (curExecBlock == nullptr) {\n    return false;\n  }\n  uint16_t instID = curExecBlock->getCurrentInstID();\n  // By internal convention, PREINST => PC == Current instruction address\n  // (not matter of architecture)\n  return curExecBlock->getInstAddress(instID) ==\n         QBDI_GPR_GET(getGPRState(), REG_PC);\n}\n\nvoid Engine::addInstrumentedRange(rword start, rword end) {\n  execBroker->addInstrumentedRange(Range<rword>(start, end, real_addr_t()));\n}\n\nbool Engine::addInstrumentedModule(const std::string &name) {\n  return execBroker->addInstrumentedModule(name);\n}\n\nbool Engine::addInstrumentedModuleFromAddr(rword addr) {\n  return execBroker->addInstrumentedModuleFromAddr(addr);\n}\n\nbool Engine::instrumentAllExecutableMaps() {\n  return execBroker->instrumentAllExecutableMaps();\n}\n\nvoid Engine::removeInstrumentedRange(rword start, rword end) {\n  execBroker->removeInstrumentedRange(Range<rword>(start, end, real_addr_t()));\n}\n\nbool Engine::removeInstrumentedModule(const std::string &name) {\n  return execBroker->removeInstrumentedModule(name);\n}\n\nbool Engine::removeInstrumentedModuleFromAddr(rword addr) {\n  return execBroker->removeInstrumentedModuleFromAddr(addr);\n}\n\nvoid Engine::removeAllInstrumentedRanges() {\n  execBroker->removeAllInstrumentedRanges();\n}\n\nstd::vector<Patch> Engine::patch(rword start) {\n  QBDI_REQUIRE_ABORT(start == strip_ptrauth(start),\n                     \"Internal Error, unsupported authenticated pointer\");\n\n  std::vector<Patch> basicBlock;\n  const LLVMCPU &llvmcpu = llvmCPUs->getCPU(curCPUMode);\n\n  // if the first address is within the execution range,\n  // stop the basic if the dissassembler went out of the range\n  size_t sizeCode = (size_t)-1;\n  const Range<rword> *curRange =\n      execBroker->getInstrumentedRange().getElementRange(start);\n  if (curRange != nullptr) {\n    sizeCode = curRange->end() - start;\n  }\n\n  const llvm::ArrayRef<uint8_t> code((uint8_t *)start, sizeCode);\n  rword address = start;\n  QBDI_DEBUG(\"Patching basic block at address 0x{:x}\", start);\n\n  bool endLoop = false;\n  // Get Basic block\n  do {\n    llvm::MCInst inst;\n    uint64_t instSize;\n    // Disassemble\n    bool dstatus = llvmcpu.getInstruction(inst, instSize,\n                                          code.slice(address - start), address);\n\n    // handle disassembly error\n    if (not dstatus) {\n      QBDI_DEBUG(\"Bump into invalid instruction at address {:x}\", address);\n\n      // Current instruction is invalid, stop the basic block right here\n      bool rollbackOK = patchRuleAssembly->earlyEnd(llvmcpu, basicBlock);\n\n      // if fail to rollback or no Patch has been generated : fail\n      if ((not rollbackOK) or (basicBlock.size() == 0)) {\n        size_t sizeDump = start + sizeCode - address;\n        if (sizeDump > 16) {\n          sizeDump = 16;\n        }\n        QBDI_ABORT(\n            \"Disassembly error : fail to parse address 0x{:x} (CPUMode {}) \"\n            \"({:n})\",\n            address, curCPUMode,\n            spdlog::to_hex(reinterpret_cast<uint8_t *>(address),\n                           reinterpret_cast<uint8_t *>(address + sizeDump)));\n      } else {\n        endLoop = true;\n        break;\n      }\n    }\n    QBDI_DEBUG(\"Disassembly address 0x{:x} ({:n})\", address,\n               spdlog::to_hex(reinterpret_cast<uint8_t *>(address),\n                              reinterpret_cast<uint8_t *>(address + instSize)));\n\n    // Generate Patch for this instruction\n    QBDI_REQUIRE_ABORT(dstatus, \"Unexpected dissassembly status\");\n    QBDI_DEBUG_BLOCK({\n      std::string disass = llvmcpu.showInst(inst, address);\n      QBDI_DEBUG(\"Patching 0x{:x} {}\", address, disass.c_str());\n    });\n    endLoop = not patchRuleAssembly->generate(inst, address, instSize, llvmcpu,\n                                              basicBlock);\n    address += instSize;\n  } while (endLoop);\n\n  QBDI_REQUIRE_ABORT(basicBlock.size() > 0,\n                     \"No instruction to dissassemble found\");\n\n  QBDI_DEBUG(\"Basic block starting at address 0x{:x} ended at address 0x{:x}\",\n             start, basicBlock.back().metadata.endAddress());\n\n  return basicBlock;\n}\n\nvoid Engine::instrument(std::vector<Patch> &basicBlock, size_t patchEnd) {\n  const LLVMCPU &llvmcpu = llvmCPUs->getCPU(curCPUMode);\n  QBDI_DEBUG(\n      \"Instrumenting sequence [0x{:x}, 0x{:x}] in basic block [0x{:x}, 0x{:x}]\",\n      basicBlock.front().metadata.address,\n      basicBlock[patchEnd - 1].metadata.address,\n      basicBlock.front().metadata.address, basicBlock.back().metadata.address);\n\n  for (size_t i = 0; i < patchEnd; i++) {\n    Patch &patch = basicBlock[i];\n    QBDI_DEBUG(\"Instrumenting {}\", patch);\n\n    // Instrument\n    for (const auto &item : instrRules) {\n      const InstrRule *rule = item.second.get();\n      if (rule->tryInstrument(patch, llvmcpu)) {\n        QBDI_DEBUG(\"Instrumentation rule {:x} applied\", item.first);\n      }\n    }\n    patch.finalizeInstsPatch();\n  }\n}\n\nvoid Engine::handleNewBasicBlock(rword pc) {\n  // disassemble and patch new basic block\n  Patch::Vec basicBlock = patch(pc);\n  // Reserve cache and get uncached instruction\n  size_t patchEnd = blockManager->preWriteBasicBlock(basicBlock);\n  // instrument uncached instruction\n  instrument(basicBlock, patchEnd);\n  // Write in the cache\n  blockManager->writeBasicBlock(std::move(basicBlock), patchEnd);\n}\n\nbool Engine::precacheBasicBlock(rword pc) {\n  QBDI_REQUIRE_ABORT(pc == strip_ptrauth(pc),\n                     \"Internal Error, unsupported authenticated pointer\");\n  QBDI_REQUIRE_ABORT(not running,\n                     \"Cannot precacheBasicBlock on a running Engine\");\n  if (blockManager->isFlushPending()) {\n    // Commit the flush\n    blockManager->flushCommit();\n  }\n#if defined(QBDI_ARCH_ARM)\n  curCPUMode = pc & 1 ? CPUMode::Thumb : CPUMode::ARM;\n  pc &= (~1);\n#endif\n  if (blockManager->getExecBlock(pc, curCPUMode) != nullptr) {\n    // already in cache\n    return false;\n  }\n  running = true;\n  handleNewBasicBlock(pc);\n  running = false;\n  return true;\n}\n\nbool Engine::run(rword start, rword stop) {\n  QBDI_REQUIRE_ABORT(\n      start == strip_ptrauth(start),\n      \"Internal Error, unsupported authenticated pointer for start pointer\");\n  QBDI_REQUIRE_ABORT(\n      stop == strip_ptrauth(stop),\n      \"Internal Error, unsupported authenticated pointer for stop pointer\");\n  QBDI_REQUIRE_ABORT(not running, \"Cannot run an already running Engine\");\n\n  rword currentPC = start;\n  bool hasRan = false;\n  bool warnAuthPC = true;\n  curGPRState = gprState.get();\n  curFPRState = fprState.get();\n\n  rword basicBlockBeginAddr = 0;\n  rword basicBlockEndAddr = 0;\n\n  // Start address is out of range\n  if (!execBroker->isInstrumented(start)) {\n    return false;\n  }\n\n  running = true;\n\n  // Execute basic block per basic block\n  do {\n    VMAction action = CONTINUE;\n\n    // If this PC is not instrumented try to transfer execution\n    if (execBroker->isInstrumented(currentPC) == false and\n        execBroker->canTransferExecution(curGPRState)) {\n\n#if defined(QBDI_ARCH_ARM)\n      // Handle ARM mode\n      // If we switch to the execbroker without an exchange, keep the current\n      // mode\n      bool changeCPUMode = curExecBlock == nullptr ||\n                           curExecBlock->getContext()->hostState.exchange == 1;\n      if (not changeCPUMode) {\n        if (curCPUMode == CPUMode::Thumb) {\n          currentPC = currentPC | 1;\n        } else {\n          currentPC = currentPC & (~1);\n        }\n      }\n#endif\n\n      curExecBlock = nullptr;\n      basicBlockBeginAddr = 0;\n      basicBlockEndAddr = 0;\n\n      QBDI_DEBUG(\"Executing 0x{:x} through execBroker\", currentPC);\n      action = signalEvent(EXEC_TRANSFER_CALL, currentPC, nullptr, 0,\n                           curGPRState, curFPRState);\n      // transfer execution\n      if (action == CONTINUE) {\n        execBroker->transferExecution(currentPC, curGPRState, curFPRState);\n        action = signalEvent(EXEC_TRANSFER_RETURN, currentPC, nullptr, 0,\n                             curGPRState, curFPRState);\n      }\n    }\n    // Else execute through DBI\n    else {\n      VMEvent event = VMEvent::SEQUENCE_ENTRY;\n\n#if defined(QBDI_ARCH_ARM)\n      // Handle ARM mode switching, only at the start of an execution or when\n      // the guest signal an exchange.\n      bool changeCPUMode = curExecBlock == nullptr ||\n                           curExecBlock->getContext()->hostState.exchange == 1;\n      if (changeCPUMode) {\n        curCPUMode = currentPC & 1 ? CPUMode::Thumb : CPUMode::ARM;\n        QBDI_DEBUG(\"CPUMode set to {}\",\n                   curCPUMode == CPUMode::ARM ? \"ARM\" : \"Thumb\");\n      } else if (curCPUMode == CPUMode::ARM) {\n        QBDI_REQUIRE_ABORT((currentPC & 1) == 0,\n                           \"Unexpected address in ARM mode\");\n      } else {\n        QBDI_REQUIRE_ABORT((currentPC & 1) == 1,\n                           \"Unexpected address in Thumb mode\");\n      }\n      currentPC = currentPC & (~1);\n#endif\n\n      QBDI_DEBUG(\"Executing 0x{:x} through DBI in mode {}\", currentPC,\n                 curCPUMode);\n\n      // Is cache flush pending?\n      if (blockManager->isFlushPending()) {\n        // Backup fprState and gprState\n        *gprState = *curGPRState;\n        *fprState = *curFPRState;\n        curGPRState = gprState.get();\n        curFPRState = fprState.get();\n        // Commit the flush\n        blockManager->flushCommit();\n      }\n\n      // Test if we have it in cache\n      SeqLoc currentSequence;\n      curExecBlock = blockManager->getProgrammedExecBlock(currentPC, curCPUMode,\n                                                          &currentSequence);\n      if (curExecBlock == nullptr) {\n        QBDI_DEBUG(\n            \"Cache miss for 0x{:x}, patching & instrumenting new basic block\",\n            currentPC);\n        handleNewBasicBlock(currentPC);\n        // Signal a new basic block\n        event |= BASIC_BLOCK_NEW;\n        // Set new basic block as current\n        curExecBlock = blockManager->getProgrammedExecBlock(\n            currentPC, curCPUMode, &currentSequence);\n        QBDI_REQUIRE_ABORT(curExecBlock != nullptr,\n                           \"Fail to instrument the next basic block\");\n      }\n\n      if (basicBlockEndAddr == 0) {\n        event |= BASIC_BLOCK_ENTRY;\n        basicBlockEndAddr = currentSequence.bbEnd;\n        basicBlockBeginAddr = currentPC;\n      }\n\n      // Set context if necessary\n      if (&(curExecBlock->getContext()->gprState) != curGPRState ||\n          &(curExecBlock->getContext()->fprState) != curFPRState) {\n        curExecBlock->getContext()->gprState = *curGPRState;\n        curExecBlock->getContext()->fprState = *curFPRState;\n      }\n      curGPRState = &(curExecBlock->getContext()->gprState);\n      curFPRState = &(curExecBlock->getContext()->fprState);\n\n      action = signalEvent(event, currentPC, &currentSequence,\n                           basicBlockBeginAddr, curGPRState, curFPRState);\n\n      if (action == CONTINUE) {\n        hasRan = true;\n        action = curExecBlock->execute();\n        // Signal events if normal exit\n        if (action == CONTINUE) {\n          if (basicBlockEndAddr == currentSequence.seqEnd) {\n            action = signalEvent(SEQUENCE_EXIT | BASIC_BLOCK_EXIT, currentPC,\n                                 &currentSequence, basicBlockBeginAddr,\n                                 curGPRState, curFPRState);\n            basicBlockBeginAddr = 0;\n            basicBlockEndAddr = 0;\n          } else {\n            action = signalEvent(SEQUENCE_EXIT, currentPC, &currentSequence,\n                                 basicBlockBeginAddr, curGPRState, curFPRState);\n          }\n        }\n      }\n    }\n    if (action == STOP) {\n      QBDI_DEBUG(\"Receive STOP Action\");\n      break;\n    }\n    if (action != CONTINUE) {\n      basicBlockBeginAddr = 0;\n      basicBlockEndAddr = 0;\n      curExecBlock = nullptr;\n    }\n    // Get next block PC\n    currentPC = QBDI_GPR_GET(curGPRState, REG_PC);\n    rword noAuthPC = strip_ptrauth(currentPC);\n    if (currentPC != noAuthPC) {\n      if (warnAuthPC) {\n        QBDI_WARN(\n            \"REG_PC value should not be authenticated pointer (get 0x{:x}, \"\n            \"should be 0x{:x})\",\n            currentPC, noAuthPC);\n        warnAuthPC = false;\n      }\n      currentPC = noAuthPC;\n    }\n\n    QBDI_DEBUG(\"Next address to execute is 0x{:x} (stop is 0x{:x})\", currentPC,\n               stop);\n  } while (currentPC != stop);\n\n  // Copy final context\n  *gprState = *curGPRState;\n  *fprState = *curFPRState;\n  curGPRState = gprState.get();\n  curFPRState = fprState.get();\n  curExecBlock = nullptr;\n  running = false;\n\n  if (blockManager->isFlushPending()) {\n    blockManager->flushCommit();\n  }\n\n  return hasRan;\n}\n\nuint32_t Engine::addInstrRule(std::unique_ptr<InstrRule> &&rule) {\n  uint32_t id = instrRulesCounter++;\n  QBDI_REQUIRE_ACTION(id < EVENTID_VM_MASK, return VMError::INVALID_EVENTID);\n\n  this->clearCache(rule->affectedRange());\n\n  auto v = std::make_pair(id, std::move(rule));\n\n  // insert rule in instrRules and keep the priority order\n  auto it = std::upper_bound(instrRules.begin(), instrRules.end(), v,\n                             [](const decltype(instrRules)::value_type &a,\n                                const decltype(instrRules)::value_type &b) {\n                               return a.second->getPriority() >\n                                      b.second->getPriority();\n                             });\n  instrRules.insert(it, std::move(v));\n\n  return id;\n}\n\nInstrRule *Engine::getInstrRule(uint32_t id) {\n  auto it = std::find_if(\n      instrRules.begin(), instrRules.end(),\n      [id](const std::pair<uint32_t, std::unique_ptr<InstrRule>> &el) {\n        return id == el.first;\n      });\n  if (it == instrRules.end()) {\n    return nullptr;\n  } else {\n    return it->second.get();\n  }\n}\n\nuint32_t Engine::addVMEventCB(VMEvent mask, VMCallback cbk, void *data) {\n  uint32_t id = vmCallbacksCounter++;\n  QBDI_REQUIRE_ACTION(id < EVENTID_VM_MASK, return VMError::INVALID_EVENTID);\n  vmCallbacks.emplace_back(id, CallbackRegistration{mask, cbk, data});\n  eventMask |= mask;\n  return id | EVENTID_VM_MASK;\n}\n\nbool Engine::setVMEventCB(uint32_t id, VMCallback cbk, void *data) {\n  auto it =\n      std::find_if(vmCallbacks.begin(), vmCallbacks.end(),\n                   [id](const std::pair<uint32_t, CallbackRegistration> &el) {\n                     return id == (el.first | EVENTID_VM_MASK);\n                   });\n  if (it == vmCallbacks.end()) {\n    return false;\n  } else {\n    it->second.cbk = cbk;\n    it->second.data = data;\n    return true;\n  }\n}\n\nVMAction Engine::signalEvent(VMEvent event, rword currentPC,\n                             const SeqLoc *seqLoc, rword basicBlockBegin,\n                             GPRState *gprState, FPRState *fprState) {\n  if ((event & eventMask) == 0) {\n    return CONTINUE;\n  }\n\n  VMState vmState{event, currentPC, currentPC, currentPC, currentPC, 0};\n  if (seqLoc != nullptr) {\n    vmState.basicBlockStart = basicBlockBegin;\n    vmState.basicBlockEnd = seqLoc->bbEnd;\n    vmState.sequenceStart = seqLoc->seqStart;\n    vmState.sequenceEnd = seqLoc->seqEnd;\n  }\n\n  VMAction action = CONTINUE;\n  for (const auto &item : vmCallbacks) {\n    const QBDI::CallbackRegistration &r = item.second;\n    if (event & r.mask) {\n      vmState.event = event;\n      VMAction res = r.cbk(vminstance, &vmState, gprState, fprState, r.data);\n      if (res > action) {\n        action = res;\n      }\n    }\n  }\n  return action;\n}\n\nconst InstAnalysis *Engine::getInstAnalysis(rword address,\n                                            AnalysisType type) const {\n\n#if defined(QBDI_ARCH_ARM)\n  CPUMode cpumode = address & 1 ? CPUMode::Thumb : CPUMode::ARM;\n  address &= (~1);\n#else\n  CPUMode cpumode = CPUMode::DEFAULT;\n#endif\n  const ExecBlock *block = blockManager->getExecBlock(address, cpumode);\n  if (block == nullptr) {\n    // not in cache\n    return nullptr;\n  }\n  uint16_t instID = block->getInstID(address, cpumode);\n  QBDI_REQUIRE_ACTION(instID != NOT_FOUND, return nullptr);\n  return block->getInstAnalysis(instID, type);\n}\n\nstd::optional<std::pair<const ExecBlock *, uint16_t>>\nEngine::getPatchInfoOfJit(rword address) const {\n  rword pageAddress = address & ~(ExecBlock::getPageSize() - 1);\n  QBDI_DEBUG(\"Search Patch address 0x{:x} with page address 0x{:x}\", address,\n             pageAddress);\n  auto *e = blockManager->getExecBlockFromJitAddress(pageAddress);\n\n  if (e == nullptr) {\n    QBDI_DEBUG(\"No ExecBlock with page address 0x{:x}\", pageAddress);\n    return {};\n  }\n  uint16_t instId = e->getPatchAddressOfJit(address);\n  if (instId == NOT_FOUND) {\n    QBDI_DEBUG(\"No Instruction in execBlock 0x{:x} with JIT address 0x{:x}\",\n               reinterpret_cast<uintptr_t>(e), address);\n    return {{e, NOT_FOUND}};\n  } else {\n    QBDI_DEBUG(\n        \"Found Instruction {} in execBlock 0x{:x} with JIT address 0x{:x}\",\n        instId, reinterpret_cast<uintptr_t>(e), address);\n    return {{e, instId}};\n  }\n}\n\nbool Engine::deleteInstrumentation(uint32_t id) {\n  if (id & EVENTID_VM_MASK) {\n    id &= ~EVENTID_VM_MASK;\n    for (size_t i = 0; i < vmCallbacks.size(); i++) {\n      if (vmCallbacks[i].first == id) {\n        vmCallbacks.erase(vmCallbacks.begin() + i);\n        return true;\n      }\n    }\n  } else {\n    for (size_t i = 0; i < instrRules.size(); i++) {\n      if (instrRules[i].first == id) {\n        this->clearCache(instrRules[i].second->affectedRange());\n        instrRules.erase(instrRules.begin() + i);\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\nvoid Engine::deleteAllInstrumentations() {\n  // clear cache\n  for (const auto &r : instrRules) {\n    this->clearCache(r.second->affectedRange());\n  }\n  instrRules.clear();\n  vmCallbacks.clear();\n  instrRulesCounter = 0;\n  vmCallbacksCounter = 0;\n  eventMask = VMEvent::NO_EVENT;\n}\n\nvoid Engine::clearAllCache() { blockManager->clearCache(not running); }\n\nvoid Engine::clearCache(rword start, rword end) {\n  blockManager->clearCache(Range<rword>(start, end, real_addr_t()));\n  if (not running && blockManager->isFlushPending()) {\n    blockManager->flushCommit();\n  }\n}\n\nvoid Engine::clearCache(RangeSet<rword> rangeSet) {\n  blockManager->clearCache(rangeSet);\n  if (not running && blockManager->isFlushPending()) {\n    blockManager->flushCommit();\n  }\n}\n\nuint32_t Engine::getNbExecBlock() const {\n  return blockManager->getNbExecBlock();\n}\n\nvoid Engine::reduceCacheTo(uint32_t nb) {\n  blockManager->reduceCacheTo(nb);\n  if (not running && blockManager->isFlushPending()) {\n    blockManager->flushCommit();\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Engine/Engine.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef ENGINE_H\n#define ENGINE_H\n\n#include <cstdlib>\n#include <memory>\n#include <optional>\n#include <stdint.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nclass LLVMCPUs;\nclass ExecBlock;\nclass ExecBlockManager;\nclass ExecBroker;\nclass InstrRule;\nclass Patch;\nclass PatchRuleAssembly;\nstruct SeqLoc;\n\nstruct CallbackRegistration {\n  VMEvent mask;\n  VMCallback cbk;\n  void *data;\n};\n\nclass Engine {\nprivate:\n  VMInstanceRef vminstance;\n\n  std::unique_ptr<LLVMCPUs> llvmCPUs;\n  std::unique_ptr<ExecBlockManager> blockManager;\n  ExecBroker *execBroker;\n  std::unique_ptr<PatchRuleAssembly> patchRuleAssembly;\n  std::vector<std::pair<uint32_t, std::unique_ptr<InstrRule>>> instrRules;\n  uint32_t instrRulesCounter;\n  std::vector<std::pair<uint32_t, CallbackRegistration>> vmCallbacks;\n  uint32_t vmCallbacksCounter;\n  std::unique_ptr<GPRState> gprState;\n  std::unique_ptr<FPRState> fprState;\n  GPRState *curGPRState;\n  FPRState *curFPRState;\n  ExecBlock *curExecBlock;\n  CPUMode curCPUMode;\n  Options options;\n  VMEvent eventMask;\n  bool running;\n\n  std::vector<Patch> patch(rword start);\n\n  void initGPRState();\n  void initFPRState();\n\n  void instrument(std::vector<Patch> &basicBlock, size_t patchEnd);\n  void handleNewBasicBlock(rword pc);\n\n  VMAction signalEvent(VMEvent kind, rword currentPC, const SeqLoc *seqLoc,\n                       rword basicBlockBegin, GPRState *gprState,\n                       FPRState *fprState);\n\npublic:\n  /*! Construct a new Engine for a given CPU with specific attributes\n   *\n   * @param[in] cpu        The name of the CPU\n   * @param[in] mattrs     A list of additional attributes\n   * @param[in] opts       The options to enable\n   * @param[in] vminstance Pointer to public engine interface\n   */\n  Engine(const std::string &cpu = \"\",\n         const std::vector<std::string> &mattrs = {},\n         Options opts = Options::NO_OPT, VMInstanceRef vminstance = nullptr);\n\n  ~Engine();\n\n  Engine(const Engine &&) = delete;\n  Engine &operator=(const Engine &&) = delete;\n\n  Engine(const Engine &);\n  Engine &operator=(const Engine &);\n\n  /*! Change the pointer to vminstance. The new pointer will be used for\n   * future callback\n   *\n   * @param[in] vminstance   The new vminstance\n   */\n  void changeVMInstanceRef(VMInstanceRef vminstance);\n\n  /*! Obtain the current general purpose register state.\n   *\n   * @return A structure containing the GPR state.\n   */\n  GPRState *getGPRState() const;\n\n  /*! Obtain the current floating point register state.\n   *\n   * @return A structure containing the FPR state.\n   */\n  FPRState *getFPRState() const;\n\n  /*! Set the GPR state\n   *\n   * @param[in] gprState A structure containing the GPR state.\n   */\n  void setGPRState(const GPRState *gprState);\n\n  /*! Set the FPR state\n   *\n   * @param[in] fprState A structure containing the FPR state.\n   */\n  void setFPRState(const FPRState *fprState);\n\n  /*! Get the current Options\n   */\n  Options getOptions() const { return options; }\n\n  /*! Set the option\n   *\n   * If the new options mismatch the current one, clearAllCache will be called.\n   *\n   * @param[in] options  New options of the engine.\n   */\n  void setOptions(Options options);\n\n  /*! Add an address range to the set of instrumented address ranges.\n   *\n   * @param[in] start  Start address of the range (included).\n   * @param[in] end    End address of the range (excluded).\n   */\n  void addInstrumentedRange(rword start, rword end);\n\n  /*! Add the executable address ranges of a module to the set of instrumented\n   * address ranges.\n   *\n   * @param[in] name  The module's name.\n   * @return  True if at least one range was added to the instrumented ranges.\n   */\n  bool addInstrumentedModule(const std::string &name);\n\n  /*! Add the executable address ranges of a module to the set of instrumented\n   * address ranges. using an address belonging to the module.\n   *\n   * @param[in] addr  An address contained by module's range.\n   * @return  True if at least one range was added to the instrumented ranges.\n   */\n  bool addInstrumentedModuleFromAddr(rword addr);\n\n  /*! Adds all the executable memory maps to the instrumented range set.\n   * @return  True if at least one range was added to the instrumented ranges.\n   */\n  bool instrumentAllExecutableMaps();\n\n  /*! Remove an address range to the set from instrumented address ranges.\n   *\n   * @param[in] start  Start address of the range (included).\n   * @param[in] end    End address of the range (excluded).\n   */\n  void removeInstrumentedRange(rword start, rword end);\n\n  /*! Remove the executable address ranges of a module from the set of\n   * instrumented address ranges.\n   *\n   * @param[in] name  The module's name.\n   * @return  True if at least one range was removed to the instrumented ranges.\n   */\n  bool removeInstrumentedModule(const std::string &name);\n\n  /*! Remove the executable address ranges of a module from the set of\n   * instrumented address ranges. using an address belonging to the module.\n   *\n   * @param[in] addr  An address contained by module's range.\n   * @return  True if at least one range was removed to the instrumented ranges.\n   */\n  bool removeInstrumentedModuleFromAddr(rword addr);\n\n  /*! Remove all instrumented ranges.\n   */\n  void removeAllInstrumentedRanges();\n\n  /*! Start the execution by the DBI.\n   *\n   * @param[in] start  Pointer to the first instruction to execute.\n   * @param[in] stop   Stop the execution when this instruction is reached.\n   * @return  True if at least one block has been executed.\n   */\n  bool run(rword start, rword stop);\n\n  /*! Add a custom instrumentation rule to the engine. Requires internal headers\n   *\n   * @param[in] rule A custom instrumentation rule.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  uint32_t addInstrRule(std::unique_ptr<InstrRule> &&rule);\n\n  /*! Get a registered instrumentation rule from the engine.\n   *\n   * @param[in] id  The id of the rule to get\n   *\n   * @return The instrumentation rule if available, else nullptr\n   */\n  InstrRule *getInstrRule(uint32_t id);\n\n  /*! Register a callback event for a specific VM event.\n   *\n   * @param[in] mask A mask of VM event type which will trigger the callback.\n   * @param[in] cbk  A function pointer to the callback.\n   * @param[in] data User defined data passed to the callback.\n   *\n   * @return The id of the registered instrumentation (or\n   * VMError::INVALID_EVENTID in case of failure).\n   */\n  uint32_t addVMEventCB(VMEvent mask, VMCallback cbk, void *data);\n\n  /*! Set a callback event for a specific VM event.\n   *\n   * @param[in] id   The id of the VMCallback to set.\n   * @param[in] cbk  A function pointer to the callback.\n   * @param[in] data User defined data passed to the callback.\n   *\n   * @return True if the id is valid and value has been set.\n   */\n  bool setVMEventCB(uint32_t id, VMCallback cbk, void *data);\n\n  /*! Remove an instrumentation.\n   *\n   * @param[in] id The id of the instrumentation to remove.\n   * @return  True if instrumentation has been removed.\n   */\n  bool deleteInstrumentation(uint32_t id);\n\n  /*! Remove all the registered instrumentations.\n   *\n   */\n  void deleteAllInstrumentations();\n\n  /*! Expose current ExecBlock\n   *\n   * @return A pointer to current ExecBlock\n   */\n  const ExecBlock *getCurExecBlock() const { return curExecBlock; }\n\n  /*! Check if current ExecBlock is PREINST in current state\n   *\n   * @return true if engine state is Pre-inst\n   */\n  bool isPreInst() const;\n\n  /*! Pre-cache a known basic block\n   *\n   * @param[in] pc Start address of a basic block\n   *\n   * @return True if basic block has been inserted in cache.\n   */\n  bool precacheBasicBlock(rword pc);\n\n  /*! Return an InstAnalysis for a cached instruction.\n   * The pointer may be invalid by any noconst method call.\n   *\n   * @param[in] address Start address of the instruction\n   * @param[in] type    type of the Analysis\n   *\n   * @return A pointer to the Analysis or a null pointer if the instruction\n   * isn't in the cache.\n   */\n  const InstAnalysis *getInstAnalysis(rword address, AnalysisType type) const;\n\n  /*! Return ExecBlock and InstID from a Jitted address.\n   * This function help to convert an address of a patched\n   * (inside a CodeSection of an ExecBlock) to the address of the instruction\n   * for which this patch was jitted.\n   *\n   * @param[in] address  Address of a Jitted instruction\n   *\n   * @return The ExecBlock and the InstId of the Jitted instruction if any\n   */\n  std::optional<std::pair<const ExecBlock *, uint16_t>>\n  getPatchInfoOfJit(rword address) const;\n\n  /*! Clear a specific address range from the translation cache.\n   *\n   * @param[in] start Start of the address range to clear from the cache.\n   * @param[in] end   End of the address range to clear from the cache.\n   *\n   */\n  void clearCache(rword start, rword end);\n\n  /*! Clear a specific address rangeSet from the translation cache.\n   *\n   * @param[in] rangeSet    The range set to clear from the cache.\n   */\n  void clearCache(RangeSet<rword> rangeSet);\n\n  /*! Clear the entire translation cache.\n   */\n  void clearAllCache();\n\n  /*! Get the number of ExecBlock in the cache. Each block uses 2 memory pages\n   * and some heap allocations.\n   *\n   * @return  The number of ExecBlock in the cache.\n   */\n  uint32_t getNbExecBlock() const;\n\n  /*! Reduce the cache to X ExecBlock. Note that this will try to purge the\n   * oldest ExecBlock first, but the block may be recreate if needed by followed\n   * execution.\n   *\n   * @param[in] nb The number of BasicBlock that should remains in the cache\n   *               after call.\n   */\n  void reduceCacheTo(uint32_t nb);\n};\n\n} // namespace QBDI\n\n#endif // ENGINE_H\n"
  },
  {
    "path": "src/Engine/LLVMCPU.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <mutex>\n#include <utility>\n\n#include \"llvm/ADT/SmallVector.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/MC/MCAsmBackend.h\"\n#include \"llvm/MC/MCAsmInfo.h\"\n#include \"llvm/MC/MCAssembler.h\"\n#include \"llvm/MC/MCCodeEmitter.h\"\n#include \"llvm/MC/MCContext.h\"\n#include \"llvm/MC/MCDisassembler/MCDisassembler.h\"\n#include \"llvm/MC/MCExpr.h\"\n#include \"llvm/MC/MCFixup.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstPrinter.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n#include \"llvm/MC/MCObjectFileInfo.h\"\n#include \"llvm/MC/MCObjectWriter.h\"\n#include \"llvm/MC/MCRegisterInfo.h\"\n#include \"llvm/MC/MCSubtargetInfo.h\"\n#include \"llvm/MC/MCTargetOptions.h\"\n#include \"llvm/MC/MCValue.h\"\n#include \"llvm/MC/TargetRegistry.h\"\n#include \"llvm/Support/TargetSelect.h\"\n#include \"llvm/Support/raw_ostream.h\"\n#include \"llvm/TargetParser/Host.h\"\n#include \"llvm/TargetParser/SubtargetFeature.h\"\n#include \"llvm/TargetParser/Triple.h\"\n\n#include \"devVariable.h\"\n#include \"QBDI/Config.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"spdlog/fmt/bin_to_hex.h\"\n\nnamespace QBDI {\n\nLLVMCPUs::LLVMCPUs(const std::string &_cpu,\n                   const std::vector<std::string> &_mattrs, Options opts) {\n#if defined(QBDI_ARCH_ARM)\n  llvmcpu[CPUMode::ARM] =\n      std::make_unique<LLVMCPU>(_cpu, \"arm\", _mattrs, opts, CPUMode::ARM);\n  llvmcpu[CPUMode::Thumb] =\n      std::make_unique<LLVMCPU>(_cpu, \"thumb\", _mattrs, opts, CPUMode::Thumb);\n#elif defined(QBDI_ARCH_AARCH64)\n  llvmcpu[CPUMode::AARCH64] = std::make_unique<LLVMCPU>(\n      _cpu, \"aarch64\", _mattrs, opts, CPUMode::AARCH64);\n#elif defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  llvmcpu[CPUMode::DEFAULT] =\n      std::make_unique<LLVMCPU>(_cpu, \"\", _mattrs, opts, CPUMode::DEFAULT);\n#endif\n}\n\nLLVMCPUs::~LLVMCPUs() = default;\n\nvoid LLVMCPUs::setOptions(Options opts) {\n  for (int i = 0; i < CPUMode::COUNT; i++) {\n    llvmcpu[i]->setOptions(opts);\n  }\n}\n\nLLVMCPU::LLVMCPU(const std::string &_cpu, const std::string &_arch,\n                 const std::vector<std::string> &_mattrs, Options opts,\n                 CPUMode cpumode)\n    : cpu(_cpu), arch(_arch), mattrs(_mattrs), options(opts), cpumode(cpumode) {\n\n  std::string error;\n  std::string featuresStr;\n\n  static std::once_flag initializeLLVMOnce;\n  std::call_once(initializeLLVMOnce, []() {\n    llvm::InitializeAllTargetInfos();\n    llvm::InitializeAllTargetMCs();\n    llvm::InitializeAllAsmParsers();\n    llvm::InitializeAllDisassemblers();\n  });\n\n  // Build features string\n  if (cpu.empty()) {\n    cpu = QBDI::getHostCPUName();\n    // If API is broken on ARM, we are facing big problems...\n    if constexpr (is_arm) {\n      QBDI_REQUIRE(!cpu.empty() && cpu != \"generic\");\n    }\n  }\n  if (mattrs.empty()) {\n    mattrs = getHostCPUFeatures();\n  }\n  if (!mattrs.empty()) {\n    llvm::SubtargetFeatures features;\n    for (unsigned i = 0; i != mattrs.size(); ++i) {\n      features.AddFeature(mattrs[i]);\n    }\n    featuresStr = features.getString();\n  }\n\n  // lookup target\n  tripleName = llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());\n  llvm::Triple processTriple(tripleName);\n  target = llvm::TargetRegistry::lookupTarget(arch, processTriple, error);\n\n  // get the new triple name once the arch has been set\n  tripleName = processTriple.getTriple();\n  QBDI_DEBUG(\"Initialized LLVM for target {}\", tripleName.c_str());\n\n  // Allocate all LLVM classes\n  llvm::MCTargetOptions MCOptions;\n  MRI = std::unique_ptr<llvm::MCRegisterInfo>(\n      target->createMCRegInfo(tripleName));\n  MAI = std::unique_ptr<llvm::MCAsmInfo>(\n      target->createMCAsmInfo(*MRI, tripleName, MCOptions));\n  MCII = std::unique_ptr<llvm::MCInstrInfo>(target->createMCInstrInfo());\n  MSTI = std::unique_ptr<llvm::MCSubtargetInfo>(\n      target->createMCSubtargetInfo(tripleName, cpu, featuresStr));\n  MCTX = std::make_unique<llvm::MCContext>(processTriple, MAI.get(), MRI.get(),\n                                           MSTI.get(), nullptr, &MCOptions);\n  MOFI = std::unique_ptr<llvm::MCObjectFileInfo>(\n      target->createMCObjectFileInfo(*MCTX, false));\n  MCTX->setObjectFileInfo(MOFI.get());\n  QBDI_DEBUG(\"Initialized LLVM subtarget with cpu {} and features {}\",\n             cpu.c_str(), featuresStr.c_str());\n\n  auto MAB = std::unique_ptr<llvm::MCAsmBackend>(\n      target->createMCAsmBackend(*MSTI, *MRI, MCOptions));\n\n  // assembler, disassembler and printer\n  null_ostream = std::make_unique<llvm::raw_null_ostream>();\n\n  disassembler = std::unique_ptr<llvm::MCDisassembler>(\n      target->createMCDisassembler(*MSTI, *MCTX));\n\n  auto codeEmitter = std::unique_ptr<llvm::MCCodeEmitter>(\n      target->createMCCodeEmitter(*MCII, *MCTX));\n\n  auto objectWriter = std::unique_ptr<llvm::MCObjectWriter>(\n      MAB->createObjectWriter(*null_ostream));\n\n  assembler = std::make_unique<llvm::MCAssembler>(\n      *MCTX, std::move(MAB), std::move(codeEmitter), std::move(objectWriter));\n\n  unsigned int variant = 0;\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  variant = ((options & Options::OPT_ATT_SYNTAX) == 0) ? 1 : 0;\n#else\n  variant = MAI->getAssemblerDialect();\n#endif\n\n  asmPrinter = std::unique_ptr<llvm::MCInstPrinter>(target->createMCInstPrinter(\n      MSTI->getTargetTriple(), variant, *MAI, *MCII, *MRI));\n  asmPrinter->setPrintImmHex(true);\n  asmPrinter->setPrintHexStyle(llvm::HexStyle::C);\n}\n\nLLVMCPU::~LLVMCPU() = default;\n\nbool LLVMCPU::getInstruction(llvm::MCInst &instr, uint64_t &size,\n                             llvm::ArrayRef<uint8_t> bytes,\n                             uint64_t address) const {\n  auto status =\n      disassembler->getInstruction(instr, size, bytes, address, llvm::nulls());\n  if (status == llvm::MCDisassembler::SoftFail) {\n    std::string dissas = showInst(instr, address);\n    QBDI_WARN(\"Disassembly softfail on 0x{:x} : {} (CPUMode {}) ({:n})\",\n              address, dissas, getCPUMode(),\n              spdlog::to_hex(bytes.data(), bytes.data() + size));\n  }\n  return status != llvm::MCDisassembler::Fail;\n}\n\nvoid LLVMCPU::writeInstruction(const llvm::MCInst inst,\n                               llvm::SmallVectorImpl<char> &CB,\n                               rword address) const {\n  // MCCodeEmitter needs a fixups array\n  llvm::SmallVector<llvm::MCFixup, 4> fixups;\n\n#if CHECK_NUM_OPERANDS\n  auto opcode = inst.getOpcode();\n  const llvm::MCInstrDesc &desc = MCII->get(opcode);\n  if (desc.isVariadic() and inst.getNumOperands() < desc.getNumOperands()) {\n    QBDI_CRITICAL(\n        \"Invalid number of operand (get {}, expect at least {}) for {} ({})\",\n        inst.getNumOperands(), desc.getNumOperands(), getInstOpcodeName(opcode),\n        opcode);\n    std::abort();\n  } else if (!desc.isVariadic() and\n             inst.getNumOperands() != desc.getNumOperands()) {\n    QBDI_CRITICAL(\"Invalid number of operand (get {}, expect {}) for {} ({})\",\n                  inst.getNumOperands(), desc.getNumOperands(),\n                  getInstOpcodeName(opcode), opcode);\n    std::abort();\n  }\n#endif\n\n  uint64_t pos = CB.size();\n  QBDI_DEBUG_BLOCK({\n    std::string disass = showInst(inst, address);\n    QBDI_DEBUG(\"Assembling {} for 0x{:x}\", disass.c_str(), address);\n  });\n  assembler->getEmitter().encodeInstruction(inst, CB, fixups, *MSTI);\n  auto buffRef = llvm::MutableArrayRef<char>(CB).drop_front(pos);\n\n  if (fixups.size() > 0) {\n    llvm::MCValue target = llvm::MCValue();\n    llvm::MCFixup fixup = fixups.pop_back_val();\n    int64_t value;\n    if (fixup.getValue()->evaluateAsAbsolute(value)) {\n      assembler->getBackend().applyFixup(*assembler, fixup, target, buffRef,\n                                         (uint64_t)value, true, MSTI.get());\n    } else {\n      QBDI_WARN(\"Could not evalutate fixup, might crash!\");\n    }\n  }\n\n  QBDI_DEBUG(\"Assembly result for 0x{:x} is: {:n}\", address,\n             spdlog::to_hex(buffRef));\n}\n\nstd::string LLVMCPU::showInst(const llvm::MCInst &inst, rword address) const {\n  std::string out;\n  llvm::raw_string_ostream rso(out);\n\n  llvm::StringRef unusedAnnotations;\n  asmPrinter->printInst(&inst, address, unusedAnnotations, *MSTI, rso);\n\n  rso.flush();\n  return out;\n}\n\nconst char *LLVMCPU::getRegisterName(RegLLVM r) const {\n  return MRI->getName(r.getValue());\n}\n\nconst char *LLVMCPU::getInstOpcodeName(const llvm::MCInst &inst) const {\n  return getInstOpcodeName(inst.getOpcode());\n}\n\nconst char *LLVMCPU::getInstOpcodeName(unsigned opcode) const {\n  /* llvm::StringRef can return a no null terminate pointer\n   * However, in the case of opcode name, that seems to be always the case\n   * see <ARM|AArch64|X86>InstrNameData\n   */\n  return MCII->getName(opcode).data();\n}\n\nvoid LLVMCPU::setOptions(Options opts) {\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  if (((opts ^ options) & Options::OPT_ATT_SYNTAX) != 0) {\n    asmPrinter =\n        std::unique_ptr<llvm::MCInstPrinter>(target->createMCInstPrinter(\n            MSTI->getTargetTriple(),\n            ((opts & Options::OPT_ATT_SYNTAX) == 0) ? 1 : 0, *MAI, *MCII,\n            *MRI));\n    asmPrinter->setPrintImmHex(true);\n    asmPrinter->setPrintHexStyle(llvm::HexStyle::C);\n  }\n#endif\n  options = opts;\n}\n\nint LLVMCPU::getMCInstSize(const llvm::MCInst &inst) const {\n  llvm::SmallVector<char, 16> stream;\n\n  writeInstruction(inst, stream);\n\n  return stream.size();\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Engine/LLVMCPU.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LLVMCPU_H\n#define LLVMCPU_H\n\n#include <algorithm>\n#include <memory>\n#include <stdint.h>\n#include <string>\n#include <vector>\n\n#include \"llvm/ADT/ArrayRef.h\"\n#include \"llvm/ADT/SmallVector.h\"\n\n#include \"QBDI/Options.h\"\n#include \"QBDI/State.h\"\n\nnamespace llvm {\nclass MCAsmInfo;\nclass MCAssembler;\nclass MCCodeEmitter;\nclass MCContext;\nclass MCDisassembler;\nclass MCInst;\nclass MCInstPrinter;\nclass MCInstrInfo;\nclass MCObjectFileInfo;\nclass MCRegisterInfo;\nclass MCSubtargetInfo;\nclass Target;\nclass raw_pwrite_stream;\n} // namespace llvm\n\nnamespace QBDI {\nstruct RegLLVM;\n\nclass LLVMCPU {\n\nprivate:\n  std::string tripleName;\n  std::string cpu;\n  std::string arch;\n  std::vector<std::string> mattrs;\n  const llvm::Target *target;\n  Options options;\n  CPUMode cpumode;\n\n  std::unique_ptr<llvm::MCAsmInfo> MAI;\n  std::unique_ptr<llvm::MCContext> MCTX;\n  std::unique_ptr<llvm::MCInstrInfo> MCII;\n  std::unique_ptr<llvm::MCObjectFileInfo> MOFI;\n  std::unique_ptr<llvm::MCRegisterInfo> MRI;\n  std::unique_ptr<llvm::MCSubtargetInfo> MSTI;\n\n  std::unique_ptr<llvm::MCAssembler> assembler;\n  std::unique_ptr<llvm::MCDisassembler> disassembler;\n  std::unique_ptr<llvm::MCInstPrinter> asmPrinter;\n  std::unique_ptr<llvm::raw_pwrite_stream> null_ostream;\n\npublic:\n  LLVMCPU(const std::string &cpu = \"\", const std::string &arch = \"\",\n          const std::vector<std::string> &mattrs = {},\n          Options opts = Options::NO_OPT, CPUMode cpumode = CPUMode::DEFAULT);\n\n  ~LLVMCPU();\n\n  LLVMCPU(const LLVMCPU &&) = delete;\n  LLVMCPU &operator=(const LLVMCPU &&) = delete;\n\n  LLVMCPU(const LLVMCPU &) = delete;\n  LLVMCPU &operator=(const LLVMCPU &) = delete;\n\n  void writeInstruction(llvm::MCInst inst, llvm::SmallVectorImpl<char> &CB,\n                        rword address = 0) const;\n\n  bool getInstruction(llvm::MCInst &inst, uint64_t &size,\n                      llvm::ArrayRef<uint8_t> bytes, uint64_t address) const;\n\n  std::string showInst(const llvm::MCInst &inst, rword address) const;\n\n  const char *getInstOpcodeName(const llvm::MCInst &inst) const;\n\n  const char *getInstOpcodeName(unsigned opcode) const;\n\n  const char *getRegisterName(RegLLVM id) const;\n\n  inline const std::string &getCPU() const { return cpu; }\n\n  inline const std::vector<std::string> &getMattrs() const { return mattrs; }\n\n  inline const CPUMode getCPUMode() const { return cpumode; }\n\n  inline operator CPUMode() const { return cpumode; }\n\n  inline const llvm::MCInstrInfo &getMCII() const { return *MCII; }\n\n  inline const llvm::MCRegisterInfo &getMRI() const { return *MRI; }\n\n  Options getOptions() const { return options; }\n\n  bool hasOptions(Options opts) const { return (getOptions() & opts) == opts; }\n\n  void setOptions(Options opts);\n\n  int getMCInstSize(const llvm::MCInst &inst) const;\n};\n\nclass LLVMCPUs {\nprivate:\n  std::unique_ptr<LLVMCPU> llvmcpu[CPUMode::COUNT];\n\npublic:\n  LLVMCPUs(const std::string &cpu = \"\",\n           const std::vector<std::string> &mattrs = {},\n           Options opts = Options::NO_OPT);\n\n  ~LLVMCPUs();\n\n  LLVMCPUs(const LLVMCPUs &&) = delete;\n  LLVMCPUs &operator=(const LLVMCPUs &&) = delete;\n\n  LLVMCPUs(const LLVMCPUs &) = delete;\n  LLVMCPUs &operator=(const LLVMCPUs &) = delete;\n\n  inline const bool isSameCPU(const LLVMCPUs &other) const {\n    return getCPU() == other.getCPU() and getMattrs() == other.getMattrs();\n  }\n\n  inline const std::string &getCPU() const { return llvmcpu[0]->getCPU(); }\n\n  inline const std::vector<std::string> &getMattrs() const {\n    return llvmcpu[0]->getMattrs();\n  }\n\n  Options getOptions() const { return llvmcpu[0]->getOptions(); }\n\n  bool hasOptions(Options opts) const { return (getOptions() & opts) == opts; }\n\n  void setOptions(Options opts);\n\n  const LLVMCPU &getCPU(CPUMode mode) const { return *llvmcpu[mode]; }\n};\n\n} // namespace QBDI\n\n#endif // LLVMCPU_H\n"
  },
  {
    "path": "src/Engine/VM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <cstdint>\n#include <memory>\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/Errors.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/PtrAuth.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n#include \"QBDI/VM.h\"\n\n#include \"Engine/Engine.h\"\n#include \"Engine/VM_internal.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/InstrRule.h\"\n#include \"Patch/InstrRules.h\"\n#include \"Patch/MemoryAccess.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/StackSwitch.h\"\n\n// Mask to identify Virtual Callback events\n#define EVENTID_VIRTCB_MASK (1UL << 31)\n\nnamespace QBDI {\n\nnamespace {\ninline void warmConflictPriority(int priority) {\n  if (priority == PRIORITY_MEMACCESS_LIMIT ||\n      priority == PRIORITY_MEMACCESS_LIMIT + 1) {\n    QBDI_WARN(\n        \"Using priority {} may conflict with QBDI internal callback for memory \"\n        \"access\",\n        priority);\n  }\n}\n} // namespace\n\nVMAction memReadGate(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n                     void *data) {\n  std::vector<std::pair<uint32_t, MemCBInfo>> &memCBInfos =\n      *static_cast<std::vector<std::pair<uint32_t, MemCBInfo>> *>(data);\n  std::vector<MemoryAccess> memAccesses = vm->getInstMemoryAccess();\n  RangeSet<rword> readRange;\n  for (const MemoryAccess &memAccess : memAccesses) {\n    if (memAccess.type & MEMORY_READ) {\n      Range<rword> accessRange(memAccess.accessAddress,\n                               memAccess.accessAddress + memAccess.size,\n                               real_addr_t());\n      readRange.add(accessRange);\n    }\n  }\n\n  VMAction action = VMAction::CONTINUE;\n  for (const auto &p : memCBInfos) {\n    // Check access type and range\n    if (p.second.type == MEMORY_READ and readRange.overlaps(p.second.range)) {\n      // Forward to virtual callback\n      VMAction ret = p.second.cbk(vm, gprState, fprState, p.second.data);\n      // Always keep the most extreme action as the return\n      if (ret > action) {\n        action = ret;\n      }\n    }\n  }\n  return action;\n}\n\nVMAction memWriteGate(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n                      void *data) {\n  std::vector<std::pair<uint32_t, MemCBInfo>> &memCBInfos =\n      *static_cast<std::vector<std::pair<uint32_t, MemCBInfo>> *>(data);\n  std::vector<MemoryAccess> memAccesses = vm->getInstMemoryAccess();\n  RangeSet<rword> readRange;\n  RangeSet<rword> writeRange;\n  for (const MemoryAccess &memAccess : memAccesses) {\n    Range<rword> accessRange(memAccess.accessAddress,\n                             memAccess.accessAddress + memAccess.size,\n                             real_addr_t());\n    if (memAccess.type & MEMORY_READ) {\n      readRange.add(accessRange);\n    }\n    if (memAccess.type & MEMORY_WRITE) {\n      writeRange.add(accessRange);\n    }\n  }\n\n  VMAction action = VMAction::CONTINUE;\n  for (const auto &p : memCBInfos) {\n    // Check accessCB\n    // 1. has MEMORY_WRITE and write range overlaps\n    // 2. is MEMORY_READ_WRITE and read range overlaps\n    // note: the case with MEMORY_READ only is managed by memReadGate\n    if (((p.second.type & MEMORY_WRITE) and\n         writeRange.overlaps(p.second.range)) or\n        (p.second.type == MEMORY_READ_WRITE and\n         readRange.overlaps(p.second.range))) {\n      // Forward to virtual callback\n      VMAction ret = p.second.cbk(vm, gprState, fprState, p.second.data);\n      // Always keep the most extreme action as the return\n      if (ret > action) {\n        action = ret;\n      }\n    }\n  }\n  return action;\n}\n\nstd::vector<InstrRuleDataCBK>\nInstrCBGateC(VMInstanceRef vm, const InstAnalysis *inst, void *_data) {\n  InstrCBInfo *data = static_cast<InstrCBInfo *>(_data);\n  std::vector<InstrRuleDataCBK> vec{};\n  data->cbk(vm, inst, &vec, data->data);\n  return vec;\n}\n\nVMAction VMCBLambdaProxy(VMInstanceRef vm, const VMState *vmState,\n                         GPRState *gprState, FPRState *fprState, void *_data) {\n  VMCbLambda &data = *static_cast<VMCbLambda *>(_data);\n  return data(vm, vmState, gprState, fprState);\n}\n\nVMAction InstCBLambdaProxy(VMInstanceRef vm, GPRState *gprState,\n                           FPRState *fprState, void *_data) {\n  InstCbLambda &data = *static_cast<InstCbLambda *>(_data);\n  return data(vm, gprState, fprState);\n}\n\nstd::vector<InstrRuleDataCBK>\nInstrRuleCBLambdaProxy(VMInstanceRef vm, const InstAnalysis *ana, void *_data) {\n  InstrRuleCbLambda &data = *static_cast<InstrRuleCbLambda *>(_data);\n  return data(vm, ana);\n}\n\nVMAction stopCallback(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n                      void *data) {\n  return VMAction::STOP;\n}\n\n// constructor\n\nVM::VM(const std::string &cpu, const std::vector<std::string> &mattrs,\n       Options opts)\n    : memoryLoggingLevel(0), memCBID(0),\n      memReadGateCBID(VMError::INVALID_EVENTID),\n      memWriteGateCBID(VMError::INVALID_EVENTID) {\n#if defined(_QBDI_ASAN_ENABLED_)\n  opts |= Options::OPT_DISABLE_FPR;\n#endif\n  engine = std::make_unique<Engine>(cpu, mattrs, opts, this);\n  memCBInfos = std::make_unique<std::vector<std::pair<uint32_t, MemCBInfo>>>();\n  instrCBInfos = std::make_unique<\n      std::vector<std::pair<uint32_t, std::unique_ptr<InstrCBInfo>>>>();\n}\n\n// destructor\n\nVM::~VM() = default;\n\n// move constructor\n\nVM::VM(VM &&vm)\n    : engine(std::move(vm.engine)), memoryLoggingLevel(vm.memoryLoggingLevel),\n      memCBInfos(std::move(vm.memCBInfos)), memCBID(vm.memCBID),\n      memReadGateCBID(vm.memReadGateCBID),\n      memWriteGateCBID(vm.memWriteGateCBID),\n      instrCBInfos(std::move(vm.instrCBInfos)),\n      vmCBData(std::move(vm.vmCBData)), instCBData(std::move(vm.instCBData)),\n      instrRuleCBData(std::move(vm.instrRuleCBData)) {\n\n  engine->changeVMInstanceRef(this);\n}\n\n// move operator\n\nVM &VM::operator=(VM &&vm) {\n  engine = std::move(vm.engine);\n  memoryLoggingLevel = vm.memoryLoggingLevel;\n  memCBInfos = std::move(vm.memCBInfos);\n  memCBID = vm.memCBID;\n  memReadGateCBID = vm.memReadGateCBID;\n  memWriteGateCBID = vm.memWriteGateCBID;\n  instrCBInfos = std::move(vm.instrCBInfos);\n  vmCBData = std::move(vm.vmCBData);\n  instCBData = std::move(vm.instCBData);\n  instrRuleCBData = std::move(vm.instrRuleCBData);\n\n  engine->changeVMInstanceRef(this);\n\n  return *this;\n}\n\n// copy constructor\n\nVM::VM(const VM &vm)\n    : engine(std::make_unique<Engine>(*vm.engine)),\n      memoryLoggingLevel(vm.memoryLoggingLevel),\n      memCBInfos(std::make_unique<std::vector<std::pair<uint32_t, MemCBInfo>>>(\n          *vm.memCBInfos)),\n      memCBID(vm.memCBID), memReadGateCBID(vm.memReadGateCBID),\n      memWriteGateCBID(vm.memWriteGateCBID), vmCBData(vm.vmCBData),\n      instCBData(vm.instCBData), instrRuleCBData(vm.instrRuleCBData) {\n\n  engine->changeVMInstanceRef(this);\n  instrCBInfos = std::make_unique<\n      std::vector<std::pair<uint32_t, std::unique_ptr<InstrCBInfo>>>>();\n  for (const auto &p : *vm.instrCBInfos) {\n    engine->deleteInstrumentation(p.first);\n    addInstrRuleRange(p.second->range.start(), p.second->range.end(),\n                      p.second->cbk, p.second->type, p.second->data);\n  }\n\n  if (memReadGateCBID != VMError::INVALID_EVENTID) {\n    InstrRule *rule = engine->getInstrRule(memReadGateCBID);\n    QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n    QBDI_REQUIRE_ABORT(rule->changeDataPtr(memCBInfos.get()),\n                       \"VM copy internal error\");\n  }\n\n  if (memWriteGateCBID != VMError::INVALID_EVENTID) {\n    InstrRule *rule = engine->getInstrRule(memWriteGateCBID);\n    QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n    QBDI_REQUIRE_ABORT(rule->changeDataPtr(memCBInfos.get()),\n                       \"VM copy internal error\");\n  }\n\n  for (auto &p : vmCBData) {\n    engine->setVMEventCB(p.first, VMCBLambdaProxy, &p.second);\n  }\n\n  for (std::pair<uint32_t, InstCbLambda> &p : instCBData) {\n    if (p.first & EVENTID_VIRTCB_MASK) {\n      uint32_t id = p.first;\n      auto it = std::find_if(memCBInfos->begin(), memCBInfos->end(),\n                             [id](const std::pair<uint32_t, MemCBInfo> &el) {\n                               return id == el.first;\n                             });\n      QBDI_REQUIRE_ABORT(it != memCBInfos->end(), \"VM copy internal error\");\n      it->second.data = &p.second;\n    } else {\n      InstrRule *rule = engine->getInstrRule(p.first);\n      QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n      QBDI_REQUIRE_ABORT(rule->changeDataPtr(&p.second),\n                         \"VM copy internal error\");\n    }\n  }\n\n  for (std::pair<uint32_t, InstrRuleCbLambda> &p : instrRuleCBData) {\n    InstrRule *rule = engine->getInstrRule(p.first);\n    QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n    QBDI_REQUIRE_ABORT(rule->changeDataPtr(&p.second),\n                       \"VM copy internal error\");\n  }\n}\n\n// Copy operator\n\nVM &VM::operator=(const VM &vm) {\n  *engine = *vm.engine;\n  *memCBInfos = *vm.memCBInfos;\n\n  memoryLoggingLevel = vm.memoryLoggingLevel;\n  memCBID = vm.memCBID;\n  memReadGateCBID = vm.memReadGateCBID;\n  memWriteGateCBID = vm.memWriteGateCBID;\n\n  instrCBInfos = std::make_unique<\n      std::vector<std::pair<uint32_t, std::unique_ptr<InstrCBInfo>>>>();\n  for (const auto &p : *vm.instrCBInfos) {\n    engine->deleteInstrumentation(p.first);\n    addInstrRuleRange(p.second->range.start(), p.second->range.end(),\n                      p.second->cbk, p.second->type, p.second->data);\n  }\n\n  if (memReadGateCBID != VMError::INVALID_EVENTID) {\n    InstrRule *rule = engine->getInstrRule(memReadGateCBID);\n    QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n    QBDI_REQUIRE_ABORT(rule->changeDataPtr(memCBInfos.get()),\n                       \"VM copy internal error\");\n  }\n\n  if (memWriteGateCBID != VMError::INVALID_EVENTID) {\n    InstrRule *rule = engine->getInstrRule(memWriteGateCBID);\n    QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n    QBDI_REQUIRE_ABORT(rule->changeDataPtr(memCBInfos.get()),\n                       \"VM copy internal error\");\n  }\n\n  vmCBData = vm.vmCBData;\n  for (auto &p : vmCBData) {\n    engine->setVMEventCB(p.first, VMCBLambdaProxy, &p.second);\n  }\n\n  instCBData = vm.instCBData;\n  for (std::pair<uint32_t, InstCbLambda> &p : instCBData) {\n    if (p.first & EVENTID_VIRTCB_MASK) {\n      uint32_t id = p.first;\n      auto it = std::find_if(memCBInfos->begin(), memCBInfos->end(),\n                             [id](const std::pair<uint32_t, MemCBInfo> &el) {\n                               return id == el.first;\n                             });\n      QBDI_REQUIRE_ABORT(it != memCBInfos->end(), \"VM copy internal error\");\n      it->second.data = &p.second;\n    } else {\n      InstrRule *rule = engine->getInstrRule(p.first);\n      QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n      QBDI_REQUIRE_ABORT(rule->changeDataPtr(&p.second),\n                         \"VM copy internal error\");\n    }\n  }\n\n  instrRuleCBData = vm.instrRuleCBData;\n  for (std::pair<uint32_t, InstrRuleCbLambda> &p : instrRuleCBData) {\n    InstrRule *rule = engine->getInstrRule(p.first);\n    QBDI_REQUIRE_ABORT(rule != nullptr, \"VM copy internal error\");\n    QBDI_REQUIRE_ABORT(rule->changeDataPtr(&p.second),\n                       \"VM copy internal error\");\n  }\n\n  engine->changeVMInstanceRef(this);\n\n  return *this;\n}\n\n// getGPRState\n\nGPRState *VM::getGPRState() const { return engine->getGPRState(); }\n\n// getFPRState\n\nFPRState *VM::getFPRState() const { return engine->getFPRState(); }\n\n// setGPRState\n\nvoid VM::setGPRState(const GPRState *gprState) {\n  QBDI_REQUIRE_ACTION(gprState != nullptr, return);\n  engine->setGPRState(gprState);\n}\n\n// setFPRState\n\nvoid VM::setFPRState(const FPRState *fprState) {\n  QBDI_REQUIRE_ACTION(fprState != nullptr, return);\n  engine->setFPRState(fprState);\n}\n\n// getOptions\n\nOptions VM::getOptions() const { return engine->getOptions(); }\n\n// setOptions\n\nvoid VM::setOptions(Options options) {\n#if defined(_QBDI_ASAN_ENABLED_)\n  options |= Options::OPT_DISABLE_FPR;\n#endif\n  engine->setOptions(options);\n}\n\n// addInstrumentedRange\n\nvoid VM::addInstrumentedRange(rword start, rword end) {\n  QBDI_REQUIRE_ACTION(strip_ptrauth(start) < strip_ptrauth(end), return);\n  engine->addInstrumentedRange(strip_ptrauth(start), strip_ptrauth(end));\n}\n\n// addInstrumentedModule\n\nbool VM::addInstrumentedModule(const std::string &name) {\n  return engine->addInstrumentedModule(name);\n}\n\n// addInstrumentedModuleFromAddr\n\nbool VM::addInstrumentedModuleFromAddr(rword addr) {\n  return engine->addInstrumentedModuleFromAddr(strip_ptrauth(addr));\n}\n\n// instrumentAllExecutableMaps\n\nbool VM::instrumentAllExecutableMaps() {\n  return engine->instrumentAllExecutableMaps();\n}\n\n// removeInstrumentedRange\n\nvoid VM::removeInstrumentedRange(rword start, rword end) {\n  QBDI_REQUIRE_ACTION(strip_ptrauth(start) < strip_ptrauth(end), return);\n  engine->removeInstrumentedRange(strip_ptrauth(start), strip_ptrauth(end));\n}\n\n// removeAllInstrumentedRanges\n\nvoid VM::removeAllInstrumentedRanges() {\n  engine->removeAllInstrumentedRanges();\n}\n\n// removeInstrumentedModule\n\nbool VM::removeInstrumentedModule(const std::string &name) {\n  return engine->removeInstrumentedModule(name);\n}\n\n// removeInstrumentedModuleFromAddr\n\nbool VM::removeInstrumentedModuleFromAddr(rword addr) {\n  return engine->removeInstrumentedModuleFromAddr(strip_ptrauth(addr));\n}\n\n// run\n\nbool VM::run(rword start, rword stop) {\n  uint32_t stopCB = addCodeAddrCB(strip_ptrauth(stop), InstPosition::PREINST,\n                                  stopCallback, nullptr);\n  bool ret = engine->run(strip_ptrauth(start), strip_ptrauth(stop));\n  deleteInstrumentation(stopCB);\n  return ret;\n}\n\n// callA\n\n#define FAKE_RET_ADDR 42\n\nbool VM::callA(rword *retval, rword function, uint32_t argNum,\n               const rword *args) {\n  GPRState *state = getGPRState();\n  QBDI_REQUIRE_ABORT(state != nullptr, \"Fail to get VM GPRState\");\n\n  // a stack pointer must be set in state\n  if (QBDI_GPR_GET(state, REG_SP) == 0) {\n    return false;\n  }\n  // push arguments in current context\n  simulateCallA(state, FAKE_RET_ADDR, argNum, args);\n  // call function\n  bool res = run(function, FAKE_RET_ADDR);\n  // get return value from current state\n  if (retval != nullptr) {\n    *retval = QBDI_GPR_GET(state, REG_RETURN);\n  }\n  return res;\n}\n\n// call\n\nbool VM::call(rword *retval, rword function, const std::vector<rword> &args) {\n  return this->callA(retval, function, args.size(), args.data());\n}\n\n// callV\n\nbool VM::callV(rword *retval, rword function, uint32_t argNum, va_list ap) {\n  std::vector<rword> args(argNum);\n  for (uint32_t i = 0; i < argNum; i++) {\n    args[i] = va_arg(ap, rword);\n  }\n\n  bool res = this->callA(retval, function, argNum, args.data());\n\n  return res;\n}\n\n// switchStackAndCallA\n\nbool VM::switchStackAndCallA(rword *retval, rword function, uint32_t argNum,\n                             const rword *args, uint32_t stackSize) {\n\n  QBDI_REQUIRE_ACTION(stackSize > 0x10000, return false);\n\n  uint8_t *fakestack = static_cast<uint8_t *>(alignedAlloc(stackSize, 16));\n  if (fakestack == nullptr) {\n    return false;\n  }\n\n  bool res =\n      switchStack(fakestack + stackSize - sizeof(rword), [&](rword stackPtr) {\n        // add a space of one integer\n        rword sp = stackPtr - sizeof(rword);\n        // align SP to 16\n        sp &= ~0xf;\n        QBDI_GPR_SET(this->getGPRState(), REG_SP, sp);\n        return this->callA(retval, function, argNum, args);\n      });\n\n  QBDI::alignedFree(fakestack);\n  return res;\n}\n\n// switchStackAndCall\n\nbool VM::switchStackAndCall(rword *retval, rword function,\n                            const std::vector<rword> &args,\n                            uint32_t stackSize) {\n  return this->switchStackAndCallA(retval, function, args.size(), args.data(),\n                                   stackSize);\n}\n\n// switchStackAndCallV\n\nbool VM::switchStackAndCallV(rword *retval, rword function, uint32_t argNum,\n                             va_list ap, uint32_t stackSize) {\n  std::vector<rword> args(argNum);\n  for (uint32_t i = 0; i < argNum; i++) {\n    args[i] = va_arg(ap, rword);\n  }\n\n  bool res = this->switchStackAndCallA(retval, function, argNum, args.data(),\n                                       stackSize);\n\n  return res;\n}\n\n// addInstrRule\n\nuint32_t VM::addInstrRule(InstrRuleCallback cbk, AnalysisType type,\n                          void *data) {\n  RangeSet<rword> r;\n  r.add(Range<rword>(0, (rword)-1, real_addr_t()));\n  return engine->addInstrRule(\n      InstrRuleUser::unique(cbk, type, data, this, std::move(r)));\n}\n\nuint32_t VM::addInstrRule(InstrRuleCallbackC cbk, AnalysisType type,\n                          void *data) {\n  InstrCBInfo *_data = new InstrCBInfo{\n      Range<rword>(0, (rword)-1, real_addr_t()), cbk, type, data};\n  uint32_t id = addInstrRule(InstrCBGateC, type, _data);\n  instrCBInfos->emplace_back(id, _data);\n  return id;\n}\n\nuint32_t VM::addInstrRule(const InstrRuleCbLambda &cbk, AnalysisType type) {\n  auto &el = instrRuleCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id = addInstrRule(InstrRuleCBLambdaProxy, type, &el.second);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addInstrRule(InstrRuleCbLambda &&cbk, AnalysisType type) {\n  auto &el = instrRuleCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id = addInstrRule(InstrRuleCBLambdaProxy, type, &el.second);\n  el.first = id;\n  return id;\n}\n\n// addInstrRuleRange\n\nuint32_t VM::addInstrRuleRange(rword start, rword end, InstrRuleCallback cbk,\n                               AnalysisType type, void *data) {\n  RangeSet<rword> r;\n  r.add(Range<rword>(start, end, auth_addr_t()));\n  return engine->addInstrRule(InstrRuleUser::unique(cbk, type, data, this, r));\n}\n\nuint32_t VM::addInstrRuleRange(rword start, rword end, InstrRuleCallbackC cbk,\n                               AnalysisType type, void *data) {\n  InstrCBInfo *_data =\n      new InstrCBInfo{Range<rword>(start, end, auth_addr_t()), cbk, type, data};\n  uint32_t id = addInstrRuleRange(start, end, InstrCBGateC, type, _data);\n  instrCBInfos->emplace_back(id, _data);\n  return id;\n}\n\nuint32_t VM::addInstrRuleRange(rword start, rword end,\n                               const InstrRuleCbLambda &cbk,\n                               AnalysisType type) {\n  auto &el = instrRuleCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id =\n      addInstrRuleRange(start, end, InstrRuleCBLambdaProxy, type, &el.second);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addInstrRuleRange(rword start, rword end, InstrRuleCbLambda &&cbk,\n                               AnalysisType type) {\n  auto &el = instrRuleCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id =\n      addInstrRuleRange(start, end, InstrRuleCBLambdaProxy, type, &el.second);\n  el.first = id;\n  return id;\n}\n\n// addInstrRuleRangeSet\n\nuint32_t VM::addInstrRuleRangeSet(RangeSet<rword> range, InstrRuleCallback cbk,\n                                  AnalysisType type, void *data) {\n  return engine->addInstrRule(\n      InstrRuleUser::unique(cbk, type, data, this, std::move(range)));\n}\n\nuint32_t VM::addInstrRuleRangeSet(RangeSet<rword> range,\n                                  const InstrRuleCbLambda &cbk,\n                                  AnalysisType type) {\n  auto &el = instrRuleCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id = addInstrRuleRangeSet(std::move(range), InstrRuleCBLambdaProxy,\n                                     type, &el.second);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addInstrRuleRangeSet(RangeSet<rword> range,\n                                  InstrRuleCbLambda &&cbk, AnalysisType type) {\n  auto &el = instrRuleCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id = addInstrRuleRangeSet(std::move(range), InstrRuleCBLambdaProxy,\n                                     type, &el.second);\n  el.first = id;\n  return id;\n}\n\n// addMnemonicCB\n\nuint32_t VM::addMnemonicCB(const char *mnemonic, InstPosition pos,\n                           InstCallback cbk, void *data, int priority) {\n  QBDI_REQUIRE_ACTION(mnemonic != nullptr, return VMError::INVALID_EVENTID);\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  warmConflictPriority(priority);\n  return engine->addInstrRule(InstrRuleBasicCBK::unique(\n      MnemonicIs::unique(mnemonic), cbk, data, pos, true, priority,\n      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));\n}\n\nuint32_t VM::addMnemonicCB(const char *mnemonic, InstPosition pos,\n                           const InstCbLambda &cbk, int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id =\n      addMnemonicCB(mnemonic, pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addMnemonicCB(const char *mnemonic, InstPosition pos,\n                           InstCbLambda &&cbk, int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id =\n      addMnemonicCB(mnemonic, pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\n// addCodeCB\n\nuint32_t VM::addCodeCB(InstPosition pos, InstCallback cbk, void *data,\n                       int priority) {\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  warmConflictPriority(priority);\n  return engine->addInstrRule(InstrRuleBasicCBK::unique(\n      True::unique(), cbk, data, pos, true, priority,\n      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));\n}\n\nuint32_t VM::addCodeCB(InstPosition pos, const InstCbLambda &cbk,\n                       int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id = addCodeCB(pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addCodeCB(InstPosition pos, InstCbLambda &&cbk, int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id = addCodeCB(pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\n// addCodeAddrCB\n\nuint32_t VM::addCodeAddrCB(rword address, InstPosition pos, InstCallback cbk,\n                           void *data, int priority) {\n  warmConflictPriority(priority);\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  return engine->addInstrRule(InstrRuleBasicCBK::unique(\n      AddressIs::unique(strip_ptrauth(address)), cbk, data, pos, true, priority,\n      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));\n}\n\nuint32_t VM::addCodeAddrCB(rword address, InstPosition pos,\n                           const InstCbLambda &cbk, int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id =\n      addCodeAddrCB(address, pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addCodeAddrCB(rword address, InstPosition pos, InstCbLambda &&cbk,\n                           int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id =\n      addCodeAddrCB(address, pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\n// addCodeRangeCB\n\nuint32_t VM::addCodeRangeCB(rword start, rword end, InstPosition pos,\n                            InstCallback cbk, void *data, int priority) {\n  warmConflictPriority(priority);\n  QBDI_REQUIRE_ACTION(start < end, return VMError::INVALID_EVENTID);\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  return engine->addInstrRule(InstrRuleBasicCBK::unique(\n      InstructionInRange::unique(strip_ptrauth(start), strip_ptrauth(end)), cbk,\n      data, pos, true, priority,\n      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));\n}\n\nuint32_t VM::addCodeRangeCB(rword start, rword end, InstPosition pos,\n                            const InstCbLambda &cbk, int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id =\n      addCodeRangeCB(start, end, pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addCodeRangeCB(rword start, rword end, InstPosition pos,\n                            InstCbLambda &&cbk, int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id =\n      addCodeRangeCB(start, end, pos, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\n// addMemAccessCB\n\nuint32_t VM::addMemAccessCB(MemoryAccessType type, InstCallback cbk, void *data,\n                            int priority) {\n  warmConflictPriority(priority);\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  recordMemoryAccess(type);\n  switch (type) {\n    case MEMORY_READ:\n      return engine->addInstrRule(InstrRuleBasicCBK::unique(\n          DoesReadAccess::unique(), cbk, data, InstPosition::PREINST, true,\n          priority, RelocTagPreInstStdCBK));\n    case MEMORY_WRITE:\n      return engine->addInstrRule(InstrRuleBasicCBK::unique(\n          DoesWriteAccess::unique(), cbk, data, InstPosition::POSTINST, true,\n          priority, RelocTagPostInstStdCBK));\n    case MEMORY_READ_WRITE:\n      return engine->addInstrRule(InstrRuleBasicCBK::unique(\n          Or::unique(conv_unique<PatchCondition>(DoesReadAccess::unique(),\n                                                 DoesWriteAccess::unique())),\n          cbk, data, InstPosition::POSTINST, true, priority,\n          RelocTagPostInstStdCBK));\n    default:\n      return VMError::INVALID_EVENTID;\n  }\n}\n\nuint32_t VM::addMemAccessCB(MemoryAccessType type, const InstCbLambda &cbk,\n                            int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id = addMemAccessCB(type, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addMemAccessCB(MemoryAccessType type, InstCbLambda &&cbk,\n                            int priority) {\n  warmConflictPriority(priority);\n  auto &el = instCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id = addMemAccessCB(type, InstCBLambdaProxy, &el.second, priority);\n  el.first = id;\n  return id;\n}\n\n// addMemAddrCB\n\nuint32_t VM::addMemAddrCB(rword address, MemoryAccessType type,\n                          InstCallback cbk, void *data) {\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  return addMemRangeCB(address, address + 1, type, cbk, data);\n}\n\nuint32_t VM::addMemAddrCB(rword address, MemoryAccessType type,\n                          const InstCbLambda &cbk) {\n  auto &el = instCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id = addMemAddrCB(address, type, InstCBLambdaProxy, &el.second);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addMemAddrCB(rword address, MemoryAccessType type,\n                          InstCbLambda &&cbk) {\n  auto &el = instCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id = addMemAddrCB(address, type, InstCBLambdaProxy, &el.second);\n  el.first = id;\n  return id;\n}\n\n// addMemRangeCB\n\nuint32_t VM::addMemRangeCB(rword start_, rword end_, MemoryAccessType type,\n                           InstCallback cbk, void *data) {\n  rword start = strip_ptrauth(start_);\n  rword end = strip_ptrauth(end_);\n  QBDI_REQUIRE_ACTION(start < end, return VMError::INVALID_EVENTID);\n  QBDI_REQUIRE_ACTION(type & MEMORY_READ_WRITE,\n                      return VMError::INVALID_EVENTID);\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  recordMemoryAccess(type);\n  if ((type == MEMORY_READ) && memReadGateCBID == VMError::INVALID_EVENTID) {\n    memReadGateCBID = engine->addInstrRule(InstrRuleBasicCBK::unique(\n        DoesReadAccess::unique(), memReadGate, memCBInfos.get(),\n        InstPosition::PREINST, true, 0, RelocTagPreInstStdCBK));\n  }\n  if ((type & MEMORY_WRITE) && memWriteGateCBID == VMError::INVALID_EVENTID) {\n    // memWriteGate manage MEMORY_WRITE and MEMORY_READ_WRITE callback\n    memWriteGateCBID = engine->addInstrRule(InstrRuleBasicCBK::unique(\n        Or::unique(conv_unique<PatchCondition>(DoesReadAccess::unique(),\n                                               DoesWriteAccess::unique())),\n        memWriteGate, memCBInfos.get(), InstPosition::POSTINST, true, 0,\n        RelocTagPostInstStdCBK));\n  }\n  uint32_t id = memCBID++;\n  QBDI_REQUIRE_ACTION(id < EVENTID_VIRTCB_MASK,\n                      return VMError::INVALID_EVENTID);\n  memCBInfos->emplace_back(\n      id | EVENTID_VIRTCB_MASK,\n      MemCBInfo{type, {start, end, real_addr_t()}, cbk, data});\n  return id | EVENTID_VIRTCB_MASK;\n}\n\nuint32_t VM::addMemRangeCB(rword start, rword end, MemoryAccessType type,\n                           const InstCbLambda &cbk) {\n  auto &el = instCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id = addMemRangeCB(start, end, type, InstCBLambdaProxy, &el.second);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addMemRangeCB(rword start, rword end, MemoryAccessType type,\n                           InstCbLambda &&cbk) {\n  auto &el = instCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id = addMemRangeCB(start, end, type, InstCBLambdaProxy, &el.second);\n  el.first = id;\n  return id;\n}\n\n// addVMEventCB\n\nuint32_t VM::addVMEventCB(VMEvent mask, VMCallback cbk, void *data) {\n  QBDI_REQUIRE_ACTION(mask != 0, return VMError::INVALID_EVENTID);\n  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);\n  return engine->addVMEventCB(mask, cbk, data);\n}\n\nuint32_t VM::addVMEventCB(VMEvent mask, const VMCbLambda &cbk) {\n  auto &el = vmCBData.emplace_front(0xffffffff, cbk);\n  uint32_t id = addVMEventCB(mask, VMCBLambdaProxy, &el.second);\n  el.first = id;\n  return id;\n}\n\nuint32_t VM::addVMEventCB(VMEvent mask, VMCbLambda &&cbk) {\n  auto &el = vmCBData.emplace_front(0xffffffff, std::move(cbk));\n  uint32_t id = addVMEventCB(mask, VMCBLambdaProxy, &el.second);\n  el.first = id;\n  return id;\n}\n\n// deleteInstrumentation\n\nbool VM::deleteInstrumentation(uint32_t id) {\n  if (id & EVENTID_VIRTCB_MASK) {\n    auto found = std::remove_if(memCBInfos->begin(), memCBInfos->end(),\n                                [id](const std::pair<uint32_t, MemCBInfo> &el) {\n                                  return id == el.first;\n                                });\n    if (found == memCBInfos->end()) {\n      return false;\n    }\n\n    memCBInfos->erase(found, memCBInfos->end());\n    instCBData.remove_if([id](const std::pair<uint32_t, InstCbLambda> &x) {\n      return x.first == id;\n    });\n    return true;\n  } else {\n    instrCBInfos->erase(\n        std::remove_if(\n            instrCBInfos->begin(), instrCBInfos->end(),\n            [id](const std::pair<uint32_t, std::unique_ptr<InstrCBInfo>> &x) {\n              return x.first == id;\n            }),\n        instrCBInfos->end());\n    vmCBData.remove_if([id](const std::pair<uint32_t, VMCbLambda> &x) {\n      return x.first == id;\n    });\n    instCBData.remove_if([id](const std::pair<uint32_t, InstCbLambda> &x) {\n      return x.first == id;\n    });\n    instrRuleCBData.remove_if(\n        [id](const std::pair<uint32_t, InstrRuleCbLambda> &x) {\n          return x.first == id;\n        });\n    return engine->deleteInstrumentation(id);\n  }\n}\n\n// deleteAllInstrumentations\n\nvoid VM::deleteAllInstrumentations() {\n  engine->deleteAllInstrumentations();\n  memReadGateCBID = VMError::INVALID_EVENTID;\n  memWriteGateCBID = VMError::INVALID_EVENTID;\n  memCBInfos->clear();\n  instrCBInfos->clear();\n  vmCBData.clear();\n  instCBData.clear();\n  instrRuleCBData.clear();\n  memoryLoggingLevel = 0;\n}\n\n// getInstAnalysis\n\nconst InstAnalysis *VM::getInstAnalysis(AnalysisType type) const {\n  const ExecBlock *curExecBlock = engine->getCurExecBlock();\n  QBDI_REQUIRE_ACTION(curExecBlock != nullptr, return nullptr);\n  uint16_t curInstID = curExecBlock->getCurrentInstID();\n  return curExecBlock->getInstAnalysis(curInstID, type);\n}\n\n// getCachedInstAnalysis\n\nconst InstAnalysis *VM::getCachedInstAnalysis(rword address,\n                                              AnalysisType type) const {\n  return engine->getInstAnalysis(strip_ptrauth(address), type);\n}\n\n// getJITInstAnalysis\nconst InstAnalysis *VM::getJITInstAnalysis(rword jitAddress,\n                                           AnalysisType type) const {\n  auto info = engine->getPatchInfoOfJit(strip_ptrauth(jitAddress));\n  if (info and info->second != NOT_FOUND) {\n    return info->first->getInstAnalysis(info->second, type);\n  } else {\n    return nullptr;\n  }\n}\n\n// recordMemoryAccess\n\nbool VM::recordMemoryAccess(MemoryAccessType type) {\n  if (type & MEMORY_READ && !(memoryLoggingLevel & MEMORY_READ)) {\n    memoryLoggingLevel |= MEMORY_READ;\n    for (auto &r : getInstrRuleMemAccessRead()) {\n      engine->addInstrRule(std::move(r));\n    }\n  }\n  if (type & MEMORY_WRITE && !(memoryLoggingLevel & MEMORY_WRITE)) {\n    memoryLoggingLevel |= MEMORY_WRITE;\n    for (auto &r : getInstrRuleMemAccessWrite()) {\n      engine->addInstrRule(std::move(r));\n    }\n  }\n  return true;\n}\n\n// getInstMemoryAccess\n\nstd::vector<MemoryAccess> VM::getInstMemoryAccess() const {\n  const ExecBlock *curExecBlock = engine->getCurExecBlock();\n  if (curExecBlock == nullptr) {\n    return {};\n  }\n  uint16_t instID = curExecBlock->getCurrentInstID();\n  std::vector<MemoryAccess> memAccess;\n  analyseMemoryAccess(*curExecBlock, instID, !engine->isPreInst(), memAccess);\n  return memAccess;\n}\n\n// getBBMemoryAccess\n\nstd::vector<MemoryAccess> VM::getBBMemoryAccess() const {\n  const ExecBlock *curExecBlock = engine->getCurExecBlock();\n  if (curExecBlock == nullptr) {\n    return {};\n  }\n  uint16_t bbID = curExecBlock->getCurrentSeqID();\n  uint16_t instID = curExecBlock->getCurrentInstID();\n  std::vector<MemoryAccess> memAccess;\n  QBDI_DEBUG(\n      \"Search MemoryAccess for Basic Block {:x} stopping at Instruction {:x}\",\n      bbID, instID);\n\n  uint16_t endInstID = curExecBlock->getSeqEnd(bbID);\n  for (uint16_t itInstID = curExecBlock->getSeqStart(bbID);\n       itInstID <= std::min(endInstID, instID); itInstID++) {\n\n    analyseMemoryAccess(*curExecBlock, itInstID,\n                        itInstID != instID || !engine->isPreInst(), memAccess);\n  }\n  return memAccess;\n}\n\n// precacheBasicBlock\n\nbool VM::precacheBasicBlock(rword pc) {\n  return engine->precacheBasicBlock(strip_ptrauth(pc));\n}\n\n// clearAllCache\n\nvoid VM::clearAllCache() { engine->clearAllCache(); }\n\n// clearCache\n\nvoid VM::clearCache(rword start, rword end) {\n  engine->clearCache(strip_ptrauth(start), strip_ptrauth(end));\n}\n\n// getNbExecBlock\n\nuint32_t VM::getNbExecBlock() const { return engine->getNbExecBlock(); }\n\n// reduceCacheTo\n\nvoid VM::reduceCacheTo(uint32_t nb) { engine->reduceCacheTo(nb); }\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Engine/VM_C.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdarg.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string>\n#include <vector>\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/Errors.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/State.h\"\n#include \"QBDI/VM.h\"\n#include \"QBDI/VM_C.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nvoid qbdi_initVM(VMInstanceRef *instance, const char *cpu, const char **mattrs,\n                 Options opts) {\n  QBDI_REQUIRE_ACTION(instance, return);\n\n  *instance = nullptr;\n  std::string cpuStr = \"\";\n  std::vector<std::string> mattrsStr;\n\n  if (cpu != nullptr) {\n    cpuStr += cpu;\n  }\n\n  if (mattrs != nullptr) {\n    for (unsigned i = 0; mattrs[i] != nullptr; i++) {\n      mattrsStr.emplace_back(mattrs[i]);\n    }\n  }\n\n  *instance = static_cast<VMInstanceRef>(new VM(cpuStr, mattrsStr, opts));\n}\n\nvoid qbdi_terminateVM(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  delete static_cast<VM *>(instance);\n}\n\nvoid qbdi_addInstrumentedRange(VMInstanceRef instance, rword start, rword end) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->addInstrumentedRange(start, end);\n}\n\nbool qbdi_addInstrumentedModule(VMInstanceRef instance, const char *name) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->addInstrumentedModule(std::string(name));\n}\n\nbool qbdi_addInstrumentedModuleFromAddr(VMInstanceRef instance, rword addr) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->addInstrumentedModuleFromAddr(addr);\n}\n\nbool qbdi_instrumentAllExecutableMaps(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->instrumentAllExecutableMaps();\n}\n\nvoid qbdi_removeInstrumentedRange(VMInstanceRef instance, rword start,\n                                  rword end) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->removeInstrumentedRange(start, end);\n}\n\nvoid qbdi_removeAllInstrumentedRanges(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->removeAllInstrumentedRanges();\n}\n\nbool qbdi_removeInstrumentedModule(VMInstanceRef instance, const char *name) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->removeInstrumentedModule(\n      std::string(name));\n}\n\nbool qbdi_removeInstrumentedModuleFromAddr(VMInstanceRef instance, rword addr) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->removeInstrumentedModuleFromAddr(addr);\n}\n\nbool qbdi_run(VMInstanceRef instance, rword start, rword stop) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->run(start, stop);\n}\n\nbool qbdi_call(VMInstanceRef instance, rword *retval, rword function,\n               uint32_t argNum, ...) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  va_list ap;\n  va_start(ap, argNum);\n  bool res = static_cast<VM *>(instance)->callV(retval, function, argNum, ap);\n  va_end(ap);\n  return res;\n}\n\nbool qbdi_callV(VMInstanceRef instance, rword *retval, rword function,\n                uint32_t argNum, va_list ap) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->callV(retval, function, argNum, ap);\n}\n\nbool qbdi_callA(VMInstanceRef instance, rword *retval, rword function,\n                uint32_t argNum, const rword *args) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->callA(retval, function, argNum, args);\n}\n\nbool qbdi_switchStackAndCall(VMInstanceRef instance, rword *retval,\n                             rword function, uint32_t stackSize,\n                             uint32_t argNum, ...) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  va_list ap;\n  va_start(ap, argNum);\n  bool res = static_cast<VM *>(instance)->switchStackAndCallV(\n      retval, function, argNum, ap, stackSize);\n  va_end(ap);\n  return res;\n}\n\nbool qbdi_switchStackAndCallV(VMInstanceRef instance, rword *retval,\n                              rword function, uint32_t stackSize,\n                              uint32_t argNum, va_list ap) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->switchStackAndCallV(\n      retval, function, argNum, ap, stackSize);\n}\n\nbool qbdi_switchStackAndCallA(VMInstanceRef instance, rword *retval,\n                              rword function, uint32_t stackSize,\n                              uint32_t argNum, const rword *args) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->switchStackAndCallA(\n      retval, function, argNum, args, stackSize);\n}\n\nGPRState *qbdi_getGPRState(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return nullptr);\n  return static_cast<VM *>(instance)->getGPRState();\n}\n\nFPRState *qbdi_getFPRState(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return nullptr);\n  return static_cast<VM *>(instance)->getFPRState();\n}\n\nuint32_t qbdi_getErrno(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return 0);\n  return static_cast<VM *>(instance)->getErrno();\n}\n\nvoid qbdi_setGPRState(VMInstanceRef instance, GPRState *gprState) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->setGPRState(gprState);\n}\n\nvoid qbdi_setFPRState(VMInstanceRef instance, FPRState *fprState) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->setFPRState(fprState);\n}\n\nvoid qbdi_setErrno(VMInstanceRef instance, uint32_t backupErrno) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->setErrno(backupErrno);\n}\n\nOptions qbdi_getOptions(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return Options::NO_OPT);\n  return static_cast<VM *>(instance)->getOptions();\n}\n\nvoid qbdi_setOptions(VMInstanceRef instance, Options options) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->setOptions(options);\n}\n\nuint32_t qbdi_addMnemonicCB(VMInstanceRef instance, const char *mnemonic,\n                            InstPosition pos, InstCallback cbk, void *data,\n                            int priority) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addMnemonicCB(mnemonic, pos, cbk, data,\n                                                    priority);\n}\n\nuint32_t qbdi_addCodeCB(VMInstanceRef instance, InstPosition pos,\n                        InstCallback cbk, void *data, int priority) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addCodeCB(pos, cbk, data, priority);\n}\n\nuint32_t qbdi_addCodeAddrCB(VMInstanceRef instance, rword address,\n                            InstPosition pos, InstCallback cbk, void *data,\n                            int priority) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addCodeAddrCB(address, pos, cbk, data,\n                                                    priority);\n}\n\nuint32_t qbdi_addCodeRangeCB(VMInstanceRef instance, rword start, rword end,\n                             InstPosition pos, InstCallback cbk, void *data,\n                             int priority) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addCodeRangeCB(start, end, pos, cbk, data,\n                                                     priority);\n}\n\nuint32_t qbdi_addMemAccessCB(VMInstanceRef instance, MemoryAccessType type,\n                             InstCallback cbk, void *data, int priority) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addMemAccessCB(type, cbk, data, priority);\n}\n\nuint32_t qbdi_addMemAddrCB(VMInstanceRef instance, rword address,\n                           MemoryAccessType type, InstCallback cbk,\n                           void *data) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addMemAddrCB(address, type, cbk, data);\n}\n\nuint32_t qbdi_addMemRangeCB(VMInstanceRef instance, rword start, rword end,\n                            MemoryAccessType type, InstCallback cbk,\n                            void *data) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addMemRangeCB(start, end, type, cbk,\n                                                    data);\n}\n\nuint32_t qbdi_addVMEventCB(VMInstanceRef instance, VMEvent mask, VMCallback cbk,\n                           void *data) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addVMEventCB(mask, cbk, data);\n}\n\nbool qbdi_deleteInstrumentation(VMInstanceRef instance, uint32_t id) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->deleteInstrumentation(id);\n}\n\nvoid qbdi_deleteAllInstrumentations(VMInstanceRef instance) {\n  QBDI_REQUIRE_ACTION(instance, return);\n  static_cast<VM *>(instance)->deleteAllInstrumentations();\n}\n\nconst InstAnalysis *qbdi_getInstAnalysis(const VMInstanceRef instance,\n                                         AnalysisType type) {\n  QBDI_REQUIRE_ACTION(instance, return nullptr);\n  return static_cast<const VM *>(instance)->getInstAnalysis(type);\n}\n\nconst InstAnalysis *qbdi_getCachedInstAnalysis(const VMInstanceRef instance,\n                                               rword address,\n                                               AnalysisType type) {\n  QBDI_REQUIRE_ACTION(instance, return nullptr);\n  return static_cast<const VM *>(instance)->getCachedInstAnalysis(address,\n                                                                  type);\n}\n\nconst InstAnalysis *qbdi_getJITInstAnalysis(const VMInstanceRef instance,\n                                            rword address, AnalysisType type) {\n  QBDI_REQUIRE_ACTION(instance, return nullptr);\n  return static_cast<const VM *>(instance)->getJITInstAnalysis(address, type);\n}\n\nbool qbdi_recordMemoryAccess(VMInstanceRef instance, MemoryAccessType type) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->recordMemoryAccess(type);\n}\n\nMemoryAccess *qbdi_getInstMemoryAccess(VMInstanceRef instance, size_t *size) {\n  QBDI_REQUIRE_ACTION(instance, return nullptr);\n  QBDI_REQUIRE_ACTION(size, return nullptr);\n  *size = 0;\n  std::vector<MemoryAccess> ma_vec =\n      static_cast<VM *>(instance)->getInstMemoryAccess();\n  // Do not allocate if no shadows\n  if (ma_vec.size() == 0) {\n    return NULL;\n  }\n  // Allocate and copy\n  *size = ma_vec.size();\n  MemoryAccess *ma_arr =\n      static_cast<MemoryAccess *>(malloc(*size * sizeof(MemoryAccess)));\n  for (size_t i = 0; i < *size; i++) {\n    ma_arr[i] = ma_vec[i];\n  }\n  return ma_arr;\n}\n\nMemoryAccess *qbdi_getBBMemoryAccess(VMInstanceRef instance, size_t *size) {\n  QBDI_REQUIRE_ACTION(instance, return nullptr);\n  QBDI_REQUIRE_ACTION(size, return nullptr);\n  *size = 0;\n  std::vector<MemoryAccess> ma_vec =\n      static_cast<VM *>(instance)->getBBMemoryAccess();\n  // Do not allocate if no shadows\n  if (ma_vec.size() == 0) {\n    return NULL;\n  }\n  // Allocate and copy\n  *size = ma_vec.size();\n  MemoryAccess *ma_arr =\n      static_cast<MemoryAccess *>(malloc(*size * sizeof(MemoryAccess)));\n  for (size_t i = 0; i < *size; i++) {\n    ma_arr[i] = ma_vec[i];\n  }\n  return ma_arr;\n}\n\nbool qbdi_precacheBasicBlock(VMInstanceRef instance, rword pc) {\n  QBDI_REQUIRE_ACTION(instance, return false);\n  return static_cast<VM *>(instance)->precacheBasicBlock(pc);\n}\n\nvoid qbdi_clearAllCache(VMInstanceRef instance) {\n  static_cast<VM *>(instance)->clearAllCache();\n}\n\nvoid qbdi_clearCache(VMInstanceRef instance, rword start, rword end) {\n  static_cast<VM *>(instance)->clearCache(start, end);\n}\n\nuint32_t qbdi_getNbExecBlock(VMInstanceRef instance) {\n  return static_cast<VM *>(instance)->getNbExecBlock();\n}\n\nvoid qbdi_reduceCacheTo(VMInstanceRef instance, uint32_t nb) {\n  static_cast<VM *>(instance)->reduceCacheTo(nb);\n}\n\nuint32_t qbdi_addInstrRule(VMInstanceRef instance, InstrRuleCallbackC cbk,\n                           AnalysisType type, void *data) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addInstrRule(cbk, type, data);\n}\n\nuint32_t qbdi_addInstrRuleRange(VMInstanceRef instance, rword start, rword end,\n                                InstrRuleCallbackC cbk, AnalysisType type,\n                                void *data) {\n  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);\n  return static_cast<VM *>(instance)->addInstrRuleRange(start, end, cbk, type,\n                                                        data);\n}\n\nvoid qbdi_addInstrRuleData(InstrRuleDataVec cbks, InstPosition position,\n                           InstCallback cbk, void *data, int priority) {\n  QBDI_REQUIRE_ACTION(cbks, return);\n  cbks->emplace_back(position, cbk, data, priority);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Engine/VM_internal.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_VM_INTERNAL_H_\n#define QBDI_VM_INTERNAL_H_\n\n#include \"QBDI/VM.h\"\n\nnamespace QBDI {\n\nstruct MemCBInfo {\n  MemoryAccessType type;\n  Range<rword> range;\n  InstCallback cbk;\n  void *data;\n};\n\nstruct InstrCBInfo {\n  Range<rword> range;\n  InstrRuleCallbackC cbk;\n  AnalysisType type;\n  void *data;\n};\n\nVMAction memReadGate(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n                     void *data);\n\nVMAction memWriteGate(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n                      void *data);\n\nstd::vector<InstrRuleDataCBK>\nInstrCBGateC(VMInstanceRef vm, const InstAnalysis *inst, void *_data);\n\nVMAction VMCBLambdaProxy(VMInstanceRef vm, const VMState *vmState,\n                         GPRState *gprState, FPRState *fprState, void *_data);\nVMAction InstCBLambdaProxy(VMInstanceRef vm, GPRState *gprState,\n                           FPRState *fprState, void *_data);\n\nstd::vector<InstrRuleDataCBK>\nInstrRuleCBLambdaProxy(VMInstanceRef vm, const InstAnalysis *ana, void *_data);\n\nVMAction stopCallback(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,\n                      void *data);\n\n} // namespace QBDI\n\n#endif // QBDI_VM_INTERNAL_H_\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/CMakeLists.txt",
    "content": "# Add QBDI target\nset(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ExecBlock_AARCH64.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/linux-android_AARCH64.s\")\n\nelseif(QBDI_PLATFORM_MACOS)\n  if(QBDI_PTRAUTH)\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/macos_AARCH64e.s\")\n  else()\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/macos_AARCH64.s\")\n  endif()\n\nelseif(QBDI_PLATFORM_IOS)\n  if(QBDI_PTRAUTH)\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ios_AARCH64e.s\")\n  else()\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ios_AARCH64.s\")\n  endif()\n\nelse()\n  message(FATAL_ERROR \"No stub for ${QBDI_PLATFORM} (${QBDI_ARCH})\")\nendif()\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/Context_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef CONTEXT_AARCH64_H\n#define CONTEXT_AARCH64_H\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n/*! AArch64 Host context.\n */\nstruct QBDI_ALIGNED(8) HostState {\n  // backup of the scratch Register\n  rword scratchRegisterValue;\n  rword currentSROffset;\n  // prologue epilogue backup\n  rword sp;\n  // jump at the end of prologue\n  rword selector;\n  // parameter for InstCallback\n  // also used by the ExecBroker\n  union {\n    rword callback;\n    rword brokerAddr;\n  };\n  rword data;\n  rword origin;\n  rword tpidr;\n  // unused\n  rword executeFlags;\n};\n\n/*! AArch64 Execution context.\n */\nstruct QBDI_ALIGNED(8) Context {\n  // hostState needs to be first for relative addressing range reasons\n  HostState hostState;\n  GPRState gprState;\n  FPRState fprState;\n};\n\n} // namespace QBDI\n\n#endif // CONTEXT_AARCH64_H\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/ExecBlock_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <memory>\n#include <stdint.h>\n#include <vector>\n\n#include \"llvm/Support/Memory.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n#include \"QBDI/VM.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/AARCH64/Context_AARCH64.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/AARCH64/ExecBlockPatch_AARCH64.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#if defined(QBDI_PLATFORM_WINDOWS)\nextern \"C\" void qbdi_runCodeBlock(void *codeBlock, QBDI::rword execflags);\n#else\nextern void qbdi_runCodeBlock(void *codeBlock,\n                              QBDI::rword execflags) asm(\"__qbdi_runCodeBlock\");\n#endif\n\nnamespace QBDI {\n\nstatic const uint32_t MINIMAL_BLOCK_SIZE = 0xc;\n\nvoid ExecBlock::selectSeq(uint16_t seqID) {\n  QBDI_REQUIRE(seqID < seqRegistry.size());\n  currentSeq = seqID;\n  currentInst = seqRegistry[currentSeq].startInstID;\n  context->hostState.currentSROffset =\n      seqRegistry[currentSeq].sr.scratchRegisterOffset;\n  // Note: selector is currently not an authenticated pointer\n  context->hostState.selector =\n      reinterpret_cast<rword>(codeBlock.base()) +\n      static_cast<rword>(instRegistry[currentInst].offset);\n  context->hostState.executeFlags = seqRegistry[currentSeq].executeFlags;\n}\n\nvoid ExecBlock::run() {\n  if constexpr (is_ios) {\n    if (isRWRXSupported()) {\n      if (not isRX()) {\n        makeRX();\n      }\n    } else {\n      // Pages are RWX on iOS\n      llvm::sys::Memory::InvalidateInstructionCache(codeBlock.base(),\n                                                    codeBlock.allocatedSize());\n    }\n  } else {\n    if (not isRX()) {\n      makeRX();\n    }\n  }\n\n  // Set BaseValue in ScratchRegister\n  context->hostState.scratchRegisterValue =\n      QBDI_GPR_GET(&context->gprState, context->hostState.currentSROffset);\n  QBDI_GPR_SET(&context->gprState, context->hostState.currentSROffset,\n               getDataBlockBase());\n\n  // Restore errno and Execute\n  // Note: codeBlock.base() is currently not an authenticated pointer\n  if (not llvmCPUs.hasOptions(Options::OPT_DISABLE_ERRNO_BACKUP)) {\n    errno = vminstance->getErrno();\n    qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n    vminstance->setErrno(errno);\n  } else {\n    qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n  }\n\n  // Restore\n  QBDI_GPR_SET(&context->gprState, context->hostState.currentSROffset,\n               context->hostState.scratchRegisterValue);\n}\n\nbool ExecBlock::writePatch(std::vector<Patch>::const_iterator seqCurrent,\n                           std::vector<Patch>::const_iterator seqEnd,\n                           const LLVMCPU &llvmcpu) {\n  const Patch &p = *seqCurrent;\n\n  QBDI_REQUIRE(p.finalize);\n\n  if (getEpilogueOffset() <= MINIMAL_BLOCK_SIZE) {\n    isFull = true;\n    return false;\n  }\n\n  // backup the current thumbScratchRegister. If the patch need a new SR, but\n  // cannot be apply, we must restore it to write the Terminator and the\n  // JmpEpilogue\n  ScratchRegisterInfo backupSR = srInfo;\n\n  // if the patch is the first Patch that doesn't not use the current SR,\n  // Don't JIT it and end the sequence now\n  if (&p == srInfo.endSRPatch) {\n    QBDI_DEBUG(\"Change the ScratchRegister (old : {})\",\n               llvmcpu.getRegisterName(backupSR.writeScratchRegister));\n    initScratchRegisterForPatch(seqCurrent, seqEnd);\n    if (not applyRelocatedInst(\n            changeScratchRegister(llvmcpu, backupSR.writeScratchRegister,\n                                  srInfo.writeScratchRegister),\n            &tagRegistry, llvmcpu, MINIMAL_BLOCK_SIZE)) {\n      QBDI_DEBUG(\"Not enough space left: rollback\");\n      srInfo = backupSR;\n      return false;\n    }\n  }\n\n  if (not applyRelocatedInst(p.insts, &tagRegistry, llvmcpu,\n                             MINIMAL_BLOCK_SIZE)) {\n    QBDI_DEBUG(\"Not enough space left: rollback\");\n    srInfo = backupSR;\n    return false;\n  }\n  return true;\n}\n\nvoid ExecBlock::initScratchRegisterForPatch(\n    std::vector<Patch>::const_iterator seqStart,\n    std::vector<Patch>::const_iterator seqEnd) {\n\n  std::set<RegLLVM> freeRegister(&GPR_ID[0], &GPR_ID[AVAILABLE_GPR]);\n\n  if constexpr (is_macos or is_ios) {\n    // x18 is reserved by the platform, we can used it\n    freeRegister.erase(GPR_ID[18]);\n  }\n\n  auto seqIt = seqStart;\n  bool splitSequence = false;\n  while (seqIt != seqEnd) {\n    // compute the reminding free register after the current instruction\n    // 1. remove the register used by the TempManager\n    std::set<RegLLVM> delta;\n    std::set_difference(freeRegister.begin(), freeRegister.end(),\n                        seqIt->tempReg.begin(), seqIt->tempReg.end(),\n                        std::inserter(delta, delta.end()));\n\n    // 2. remove the register used by the instruction\n    for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n      if (seqIt->regUsage[i] != 0) {\n        delta.erase(GPR_ID[i]);\n      }\n    }\n\n    // check if some free register remain.\n    if (delta.empty()) {\n      splitSequence = true;\n      break;\n    } else {\n      freeRegister = std::move(delta);\n      seqIt++;\n    }\n  }\n\n  QBDI_REQUIRE_ABORT(not freeRegister.empty(),\n                     \"ScratchRegister internal error\");\n  QBDI_REQUIRE_ABORT(seqIt != seqStart, \"ScratchRegister internal error\");\n\n  // Set the position to split the sequence if a global scratch register is not\n  // avaialble\n  if (splitSequence) {\n    QBDI_DEBUG(\n        \"No Scratch register found for the whole sequence.\"\n        \"Split at a distance of {} / {}\",\n        std::distance(seqStart, seqIt), std::distance(seqStart, seqEnd));\n    srInfo.endSRPatch = &*seqIt;\n  } else {\n    QBDI_REQUIRE_ABORT(seqIt == seqEnd, \"ScratchRegister internal error\");\n    QBDI_DEBUG(\"Scratch register found for the {} Patch.\",\n               std::distance(seqStart, seqEnd));\n    srInfo.endSRPatch = nullptr;\n  }\n\n  // get a free register\n  // In order to improve the debug, we select the higher free register\n  srInfo.writeScratchRegister =\n      *std::max_element(freeRegister.begin(), freeRegister.end());\n\n  QBDI_DEBUG_BLOCK({\n    const LLVMCPU &llvmcpu = llvmCPUs.getCPU(seqStart->metadata.cpuMode);\n    QBDI_DEBUG(\"Select {} as Scratch Register\",\n               llvmcpu.getRegisterName(srInfo.writeScratchRegister));\n  });\n}\n\nvoid ExecBlock::finalizeScratchRegisterForPatch() {\n  instRegistry.back().sr.scratchRegisterOffset =\n      getGPRPosition(srInfo.writeScratchRegister);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/ScratchRegisterInfo_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef SCRATCHREGISTERINFO_AARCH64_H\n#define SCRATCHREGISTERINFO_AARCH64_H\n\n#include <stdint.h>\n\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass Patch;\n\nstruct ScratchRegisterInfo {\n  // WriteSequence variable\n  const Patch *endSRPatch;\n  RegLLVM writeScratchRegister;\n};\n\nstruct ScratchRegisterSeqInfo {\n  uint8_t scratchRegisterOffset;\n};\n\n} // namespace QBDI\n\n#endif // SCRATCHREGISTERINFO_AARCH64_H\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/ios_AARCH64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.global __qbdi_runCodeBlock\n\n.align 4\n__qbdi_runCodeBlock:\n    // Function prologue: Save general-purpose registers and stack frame\n    // Save x29 (FP) and x30 (LR) to stack and update stack pointer\n    // Save x19-x28, which are callee-saved registers\n    stp x29, x30, [sp, -16]!\n    stp x27, x28, [sp, -16]!\n    stp x25, x26, [sp, -16]!\n    stp x23, x24, [sp, -16]!\n    stp x21, x22, [sp, -16]!\n    stp x19, x20, [sp, -16]!\n\n    // Save floating-point registers (FPRs)\n    // Save d8-d15, which are callee-saved registers\n    stp d14, d15, [sp, -16]!\n    stp d12, d13, [sp, -16]!\n    stp d10, d11, [sp, -16]!\n    stp d8, d9, [sp, -16]!\n    \n    // Call function\n    // r0 (ARM32) corresponds to x0 (ARM64), with the function address stored in x0\n    blr x0;\n\n    // Function epilogue: Restore floating-point registers\n    ldp d8, d9, [sp], 16\n    ldp d10, d11, [sp], 16\n    ldp d12, d13, [sp], 16\n    ldp d14, d15, [sp], 16\n\n    // Restore general-purpose registers and stack frames\n    ldp x19, x20, [sp], 16\n    ldp x21, x22, [sp], 16\n    ldp x23, x24, [sp], 16\n    ldp x25, x26, [sp], 16\n    ldp x27, x28, [sp], 16\n    ldp x29, x30, [sp], 16\n    \n    // return\n    ret"
  },
  {
    "path": "src/ExecBlock/AARCH64/ios_AARCH64e.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.global __qbdi_runCodeBlock\n\n.align 4\n__qbdi_runCodeBlock:\n    pacibsp\n    // Function prologue: Save general-purpose registers and stack frame\n    // Save x29 (FP) and x30 (LR) to stack and update stack pointer\n    // Save x19-x28, which are callee-saved registers\n    stp x29, x30, [sp, -16]!\n    stp x27, x28, [sp, -16]!\n    stp x25, x26, [sp, -16]!\n    stp x23, x24, [sp, -16]!\n    stp x21, x22, [sp, -16]!\n    stp x19, x20, [sp, -16]!\n\n    // Save floating-point registers (FPRs)\n    // Save d8-d15, which are callee-saved registers\n    stp d14, d15, [sp, -16]!\n    stp d12, d13, [sp, -16]!\n    stp d10, d11, [sp, -16]!\n    stp d8, d9, [sp, -16]!\n    \n    // Call function\n    // r0 (ARM32) corresponds to x0 (ARM64), with the function address stored in x0\n    blr x0;\n\n    // Function epilogue: Restore floating-point registers\n    ldp d8, d9, [sp], 16\n    ldp d10, d11, [sp], 16\n    ldp d12, d13, [sp], 16\n    ldp d14, d15, [sp], 16\n\n    // Restore general-purpose registers and stack frames\n    ldp x19, x20, [sp], 16\n    ldp x21, x22, [sp], 16\n    ldp x23, x24, [sp], 16\n    ldp x25, x26, [sp], 16\n    ldp x27, x28, [sp], 16\n    ldp x29, x30, [sp], 16\n    \n    // return\n    retab\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/linux-android_AARCH64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.hidden __qbdi_runCodeBlock\n.globl  __qbdi_runCodeBlock\n\n.align 8\n__qbdi_runCodeBlock:\n\n  # Support BTI\n  # ===========\n  bti c;\n\n  # Push GPR\n  # ========\n  # see https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#611general-purpose-registers\n    # caller-saved register\n    #stp x0,  x1,  [sp, #-16]!;\n    #stp x2,  x3,  [sp, #-16]!;\n    #stp x4,  x5,  [sp, #-16]!;\n    #stp x6,  x7,  [sp, #-16]!;\n    #stp x8,  x9,  [sp, #-16]!;\n    #stp x10, x11, [sp, #-16]!;\n    #stp x12, x13, [sp, #-16]!;\n    #stp x14, x15, [sp, #-16]!;\n    #stp x16, x17, [sp, #-16]!;\n  # callee-saved register\n  stp x18, x19, [sp, #-16]!;\n  stp x20, x21, [sp, #-16]!;\n  stp x22, x23, [sp, #-16]!;\n  stp x24, x25, [sp, #-16]!;\n  stp x26, x27, [sp, #-16]!;\n  stp x28, x29, [sp, #-16]!;\n  stp x30, x0,  [sp, #-16]!;\n\n\n  # Save SIMD registers\n  # ===================\n  # see https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#612simd-and-floating-point-registers\n  # only need to saved the bottom 64 bits of v8 to v15\n\n  sub sp, sp, #64;\n  mov x8, sp;\n\n  st1 {v8.1d-v11.1d},  [x8], #32;\n  st1 {v12.1d-v15.1d}, [x8], #32;\n\n  # Call ExecBlock prologue\n  # =======================\n  blr x0;\n  # =======================\n\n  # Load SIMD registers\n  # ===================\n  ld1     {v8.1d-v11.1d},  [sp], #32;\n  ld1     {v12.1d-v15.1d}, [sp], #32;\n\n  # Pop GPR\n  # =======\n  ldp x30, x0,  [sp], 16;\n  ldp x28, x29, [sp], 16;\n  ldp x26, x27, [sp], 16;\n  ldp x24, x25, [sp], 16;\n  ldp x22, x23, [sp], 16;\n  ldp x20, x21, [sp], 16;\n  ldp x18, x19, [sp], 16;\n    # caller-saved register\n    #ldp x16, x17, [sp], 16;\n    #ldp x14, x15, [sp], 16;\n    #ldp x12, x13, [sp], 16;\n    #ldp x10, x11, [sp], 16;\n    #ldp x8,  x9,  [sp], 16;\n    #ldp x6,  x7,  [sp], 16;\n    #ldp x4,  x5,  [sp], 16;\n    #ldp x2,  x3,  [sp], 16;\n    #ldp x0,  x1,  [sp], 16;\n\n  ret;\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",@progbits\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/macos_AARCH64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.private_extern __qbdi_runCodeBlock\n\n.align 8\n__qbdi_runCodeBlock:\n\n  # Support BTI\n  # ===========\n  bti c;\n\n  # Push GPR\n  # ========\n  # see https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#611general-purpose-registers\n  # OSX specify that x18 should not be used, we don't restore or backup it\n  # see https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms\n    # caller-saved register\n    #stp x0,  x1,  [sp, #-16]!;\n    #stp x2,  x3,  [sp, #-16]!;\n    #stp x4,  x5,  [sp, #-16]!;\n    #stp x6,  x7,  [sp, #-16]!;\n    #stp x8,  x9,  [sp, #-16]!;\n    #stp x10, x11, [sp, #-16]!;\n    #stp x12, x13, [sp, #-16]!;\n    #stp x14, x15, [sp, #-16]!;\n    #stp x16, x17, [sp, #-16]!;\n  # callee-saved register\n  stp x19, x20, [sp, #-16]!;\n  stp x21, x22, [sp, #-16]!;\n  stp x23, x24, [sp, #-16]!;\n  stp x25, x26, [sp, #-16]!;\n  stp x27, x28, [sp, #-16]!;\n  stp x29, x30, [sp, #-16]!;\n\n\n  # Save SIMD registers\n  # ===================\n  # see https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#612simd-and-floating-point-registers\n  # only need to saved the bottom 64 bits of v8 to v15\n\n  sub sp, sp, #64;\n  mov x8, sp;\n\n  st1 {v8.1d-v11.1d},  [x8], #32;\n  st1 {v12.1d-v15.1d}, [x8], #32;\n\n  # Call ExecBlock prologue\n  # =======================\n  blr x0;\n  # =======================\n\n  # Load SIMD registers\n  # ===================\n  ld1     {v8.1d-v11.1d},  [sp], #32;\n  ld1     {v12.1d-v15.1d}, [sp], #32;\n\n  # Pop GPR\n  # =======\n  ldp x29, x30, [sp], 16;\n  ldp x27, x28, [sp], 16;\n  ldp x25, x26, [sp], 16;\n  ldp x23, x24, [sp], 16;\n  ldp x21, x22, [sp], 16;\n  ldp x19, x20, [sp], 16;\n    # caller-saved register\n    #ldp x16, x17, [sp], 16;\n    #ldp x14, x15, [sp], 16;\n    #ldp x12, x13, [sp], 16;\n    #ldp x10, x11, [sp], 16;\n    #ldp x8,  x9,  [sp], 16;\n    #ldp x6,  x7,  [sp], 16;\n    #ldp x4,  x5,  [sp], 16;\n    #ldp x2,  x3,  [sp], 16;\n    #ldp x0,  x1,  [sp], 16;\n\n  ret;\n\n"
  },
  {
    "path": "src/ExecBlock/AARCH64/macos_AARCH64e.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.private_extern __qbdi_runCodeBlock\n\n.align 8\n__qbdi_runCodeBlock:\n\n  # Support BTI\n  # ===========\n  bti c;\n  pacibsp;\n\n  # Push GPR\n  # ========\n  # see https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#611general-purpose-registers\n  # OSX specify that x18 should not be used, we don't restore or backup it\n  # see https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms\n    # caller-saved register\n    #stp x0,  x1,  [sp, #-16]!;\n    #stp x2,  x3,  [sp, #-16]!;\n    #stp x4,  x5,  [sp, #-16]!;\n    #stp x6,  x7,  [sp, #-16]!;\n    #stp x8,  x9,  [sp, #-16]!;\n    #stp x10, x11, [sp, #-16]!;\n    #stp x12, x13, [sp, #-16]!;\n    #stp x14, x15, [sp, #-16]!;\n    #stp x16, x17, [sp, #-16]!;\n  # callee-saved register\n  stp x19, x20, [sp, #-16]!;\n  stp x21, x22, [sp, #-16]!;\n  stp x23, x24, [sp, #-16]!;\n  stp x25, x26, [sp, #-16]!;\n  stp x27, x28, [sp, #-16]!;\n  stp x29, x30, [sp, #-16]!;\n\n\n  # Save SIMD registers\n  # ===================\n  # see https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#612simd-and-floating-point-registers\n  # only need to saved the bottom 64 bits of v8 to v15\n\n  sub sp, sp, #64;\n  mov x8, sp;\n\n  st1 {v8.1d-v11.1d},  [x8], #32;\n  st1 {v12.1d-v15.1d}, [x8], #32;\n\n  # Call ExecBlock prologue\n  # =======================\n  blr x0;\n  # =======================\n\n  # Load SIMD registers\n  # ===================\n  ld1     {v8.1d-v11.1d},  [sp], #32;\n  ld1     {v12.1d-v15.1d}, [sp], #32;\n\n  # Pop GPR\n  # =======\n  ldp x29, x30, [sp], 16;\n  ldp x27, x28, [sp], 16;\n  ldp x25, x26, [sp], 16;\n  ldp x23, x24, [sp], 16;\n  ldp x21, x22, [sp], 16;\n  ldp x19, x20, [sp], 16;\n    # caller-saved register\n    #ldp x16, x17, [sp], 16;\n    #ldp x14, x15, [sp], 16;\n    #ldp x12, x13, [sp], 16;\n    #ldp x10, x11, [sp], 16;\n    #ldp x8,  x9,  [sp], 16;\n    #ldp x6,  x7,  [sp], 16;\n    #ldp x4,  x5,  [sp], 16;\n    #ldp x2,  x3,  [sp], 16;\n    #ldp x0,  x1,  [sp], 16;\n\n  retab;\n\n"
  },
  {
    "path": "src/ExecBlock/ARM/CMakeLists.txt",
    "content": "# Add QBDI target\nset(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ExecBlock_ARM.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/linux-android_ARM.s\")\n\nelseif(QBDI_PLATFORM_IOS)\n  list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ios_ARM.asm\")\n\nelse()\n  message(FATAL_ERROR \"No stub for ${QBDI_PLATFORM} (${QBDI_ARCH})\")\nendif()\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/ExecBlock/ARM/Context_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef CONTEXT_ARM_H\n#define CONTEXT_ARM_H\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n/*! ARM Host context.\n */\nstruct QBDI_ALIGNED(4) HostState {\n  rword sp;\n  rword selector;\n  rword callback;\n  rword data;\n  rword origin;\n  rword exchange;\n  rword executeFlags;\n  // for thumb only\n  rword scratchRegisterValue;\n  rword currentSROffset;\n};\n\n/*! ARM Execution context.\n */\nstruct QBDI_ALIGNED(4) Context {\n  // hostState needs to be first for relative addressing range reasons\n  HostState hostState;\n  FPRState fprState;\n  GPRState gprState;\n};\n\n} // namespace QBDI\n\n#endif // CONTEXT_ARM_H\n"
  },
  {
    "path": "src/ExecBlock/ARM/ExecBlock_ARM.cpp",
    "content": "\n/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <sstream>\n\n#include \"QBDI/State.h\"\n#include \"QBDI/VM.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ARM/Context_ARM.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/ARM/ExecBlockPatch_ARM.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\nextern void qbdi_runCodeBlock(void *codeBlock,\n                              QBDI::rword execflags) asm(\"__qbdi_runCodeBlock\")\n    __attribute__((target(\"arm\")));\n\nnamespace QBDI {\n\nstatic const uint32_t MINIMAL_BLOCK_SIZE = 0x18;\n\nvoid ExecBlock::selectSeq(uint16_t seqID) {\n  QBDI_REQUIRE(seqID < seqRegistry.size());\n  currentSeq = seqID;\n  currentInst = seqRegistry[currentSeq].startInstID;\n  srInfo.cpuMode = seqRegistry[currentSeq].cpuMode;\n  rword selector = reinterpret_cast<rword>(codeBlock.base()) +\n                   static_cast<rword>(instRegistry[currentInst].offset);\n  if (srInfo.cpuMode == CPUMode::Thumb) {\n    context->hostState.currentSROffset =\n        seqRegistry[currentSeq].sr.scratchRegisterOffset;\n    selector |= 1;\n  } else {\n    selector &= (~1);\n  }\n  context->hostState.selector = selector;\n  context->hostState.executeFlags = seqRegistry[currentSeq].executeFlags;\n  context->hostState.exchange = static_cast<rword>(0);\n}\n\nvoid ExecBlock::run() {\n  if constexpr (is_ios) {\n    if (isRWRXSupported()) {\n      if (not isRX()) {\n        makeRX();\n      }\n    } else {\n      // Pages are RWX on iOS\n      llvm::sys::Memory::InvalidateInstructionCache(codeBlock.base(),\n                                                    codeBlock.allocatedSize());\n    }\n  } else {\n    if (not isRX()) {\n      makeRX();\n    }\n  }\n\n  if (srInfo.cpuMode == CPUMode::Thumb) {\n    // Set BaseValue in ScratchRegister\n    context->hostState.scratchRegisterValue =\n        QBDI_GPR_GET(&context->gprState, context->hostState.currentSROffset);\n    QBDI_GPR_SET(&context->gprState, context->hostState.currentSROffset,\n                 getDataBlockBase());\n\n    // Restore errno and Execute\n    if (not llvmCPUs.hasOptions(Options::OPT_DISABLE_ERRNO_BACKUP)) {\n      errno = vminstance->getErrno();\n      qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n      vminstance->setErrno(errno);\n    } else {\n      qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n    }\n\n    // Restore\n    QBDI_GPR_SET(&context->gprState, context->hostState.currentSROffset,\n                 context->hostState.scratchRegisterValue);\n  } else {\n    // Restore errno and Execute\n    if (not llvmCPUs.hasOptions(Options::OPT_DISABLE_ERRNO_BACKUP)) {\n      errno = vminstance->getErrno();\n      qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n      vminstance->setErrno(errno);\n    } else {\n      qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n    }\n  }\n}\n\nbool ExecBlock::writePatch(std::vector<Patch>::const_iterator seqCurrent,\n                           std::vector<Patch>::const_iterator seqEnd,\n                           const LLVMCPU &llvmcpu) {\n  const Patch &p = *seqCurrent;\n  QBDI_REQUIRE(p.finalize);\n\n  if (getEpilogueOffset() <= MINIMAL_BLOCK_SIZE) {\n    isFull = true;\n    return false;\n  }\n  if (llvmcpu == CPUMode::ARM) {\n    // the code should be able to allocate and access to at least one shadow\n    // In ARM mode, LDR can have a range of -4095 to 4095. As PC is always read\n    // as PC+8, we should be able to access the first available shadow with\n    // PC+8+4092.\n    const unsigned minPosition =\n        sizeof(Context) - 4 + sizeof(rword) * shadowIdx;\n    unsigned targetAddress = std::max(codeBlockPosition, minPosition);\n    // align to 4\n    targetAddress = (targetAddress + 3) & (~3);\n    if (codeBlockPosition < targetAddress) {\n      // In ARM, [0, 0, 0, 0] is a NOP instruction : andeq r0, r0, r0\n      // Write as much NOP instructions as needed.\n      if (not writeCodeByte(llvm::SmallVector<char, 16>(\n              targetAddress - codeBlockPosition, 0))) {\n        QBDI_DEBUG(\"Not enough space left: rollback\");\n        return false;\n      }\n    }\n  }\n\n  // backup the current thumbScratchRegister. If the patch need a new SR, but\n  // cannot be apply, we must restore it to write the Terminator and the\n  // JmpEpilogue\n  ScratchRegisterInfo backupSR = srInfo;\n\n  // if the patch is the first Patch that doesn't not use the current SR,\n  // Don't JIT it and end the sequence now\n  if (llvmcpu == CPUMode::Thumb and &p == srInfo.endSRPatch) {\n    QBDI_DEBUG(\"Change the ScratchRegister (old : {})\",\n               llvmcpu.getRegisterName(backupSR.thumbScratchRegister));\n    initScratchRegisterForPatch(seqCurrent, seqEnd);\n    if (not applyRelocatedInst(\n            changeScratchRegister(llvmcpu, backupSR.thumbScratchRegister,\n                                  srInfo.thumbScratchRegister),\n            &tagRegistry, llvmcpu, MINIMAL_BLOCK_SIZE)) {\n      QBDI_DEBUG(\"Not enough space left: rollback\");\n      srInfo = backupSR;\n      return false;\n    }\n  }\n\n  if (not applyRelocatedInst(p.insts, &tagRegistry, llvmcpu,\n                             MINIMAL_BLOCK_SIZE)) {\n    QBDI_DEBUG(\"Not enough space left: rollback\");\n    srInfo = backupSR;\n    return false;\n  }\n  return true;\n}\n\nvoid ExecBlock::initScratchRegisterForPatch(\n    std::vector<Patch>::const_iterator seqStart,\n    std::vector<Patch>::const_iterator seqEnd) {\n\n  if (seqStart->metadata.cpuMode == CPUMode::ARM) {\n    // no need of ScratchRegister.\n    srInfo.thumbScratchRegister = GPR_ID[0];\n    srInfo.endSRPatch = nullptr;\n    return;\n  }\n\n  std::set<RegLLVM> freeRegister(&GPR_ID[0], &GPR_ID[AVAILABLE_GPR]);\n\n  auto seqIt = seqStart;\n  bool splitSequence = false;\n  while (seqIt != seqEnd) {\n    // compute the reminding free register after the current instruction\n    // 1. remove the register used by the TempManager\n    std::set<RegLLVM> delta;\n    std::set_difference(freeRegister.begin(), freeRegister.end(),\n                        seqIt->tempReg.begin(), seqIt->tempReg.end(),\n                        std::inserter(delta, delta.end()));\n\n    // 2. remove the register used by the instruction and not allow to be a\n    //    scratch register (case with LDM/STM instruction)\n    for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n      if ((seqIt->regUsage[i] & RegisterUsage::RegisterBoth) != 0 and\n          (seqIt->regUsage[i] & RegisterUsage::RegisterSavedScratch) == 0) {\n        delta.erase(GPR_ID[i]);\n      }\n    }\n\n    // check if some free register remain.\n    if (delta.empty()) {\n      splitSequence = true;\n      break;\n    } else {\n      freeRegister = std::move(delta);\n      seqIt++;\n    }\n  }\n\n  QBDI_REQUIRE_ABORT(not freeRegister.empty(),\n                     \"ScratchRegister internal error\");\n  QBDI_REQUIRE_ABORT(seqIt != seqStart, \"ScratchRegister internal error\");\n\n  // Set the position to split the sequence if a global scratch register is not\n  // avaialble\n  if (splitSequence) {\n    QBDI_DEBUG(\n        \"No Scratch register found for the whole sequence.\"\n        \"Split at a distance of {} / {}\",\n        std::distance(seqStart, seqIt), std::distance(seqStart, seqEnd));\n    srInfo.endSRPatch = &*seqIt;\n  } else {\n    QBDI_REQUIRE_ABORT(seqIt == seqEnd, \"ScratchRegister internal error\");\n    QBDI_DEBUG(\"Scratch register found for the {} Patch.\",\n               std::distance(seqStart, seqEnd));\n    srInfo.endSRPatch = nullptr;\n  }\n\n  // get a free register\n  // In order to improve the debug, we select the higher free register\n  srInfo.thumbScratchRegister =\n      *std::max_element(freeRegister.begin(), freeRegister.end());\n\n  QBDI_DEBUG_BLOCK({\n    const LLVMCPU &llvmcpu = llvmCPUs.getCPU(seqStart->metadata.cpuMode);\n    QBDI_DEBUG(\"Select {} as Scratch Register\",\n               llvmcpu.getRegisterName(srInfo.thumbScratchRegister));\n  });\n}\n\nvoid ExecBlock::finalizeScratchRegisterForPatch() {\n  instRegistry.back().sr.scratchRegisterOffset =\n      getGPRPosition(srInfo.thumbScratchRegister);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBlock/ARM/ScratchRegisterInfo_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef SCRATCHREGISTERINFO_ARM_H\n#define SCRATCHREGISTERINFO_ARM_H\n\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass Patch;\n\nstruct ScratchRegisterInfo {\n  // Execution variable\n  CPUMode cpuMode;\n\n  // WriteSequence variable\n  const Patch *endSRPatch;\n  RegLLVM thumbScratchRegister;\n};\n\nstruct ScratchRegisterSeqInfo {\n  uint8_t scratchRegisterOffset;\n};\n\n} // namespace QBDI\n\n#endif // SCRATCHREGISTERINFO_ARM_H\n"
  },
  {
    "path": "src/ExecBlock/ARM/ios_ARM.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.code 32\n.section .text\n.align 4\n.globl __qbdi_runCodeBlock\n\n.type __qbdi_runCodeBlock, %function\n__qbdi_runCodeBlock:\n    // save GPRs\n    push {r0-r12,lr};\n    // save FPRs\n    vpush {s0-s7}\n    vpush {s16-s31}\n    // call block\n    blx r0;\n    // restore FPRs\n    vpop    {s16-s31}\n    vpop    {s0-s7}\n    // restore GPR and ret\n    pop {r0-r12,pc};\n"
  },
  {
    "path": "src/ExecBlock/ARM/linux-android_ARM.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n;#if defined(__ARM_PCS_VFP)\n;.fpu vfp\n.fpu neon\n\n;#endif\n\n.code 32\n.section .text\n.align 4\n.hidden __qbdi_runCodeBlock\n.globl  __qbdi_runCodeBlock\n.type __qbdi_runCodeBlock, %function\n\n__qbdi_runCodeBlock:\n    // save GPRs\n    push {r0-r12,lr};\n\n;#if defined(__ARM_PCS_VFP)\n    // save FPRs\n    // https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#6121vfp-register-usage-conventions\n    // save only s16-s31 (also named d8-d15)\n    vpush {s16-s31}\n    //vmrs r2, FPSCR\n    //push {r2}\n    //vmrs r2, FPEXC\n    //push {r2}\n;#endif\n\n    // call block\n    blx r0;\n\n;#if defined(__ARM_PCS_VFP)\n    // restore FPRs\n    // https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#6121vfp-register-usage-conventions\n    //pop {r2}\n    //vmsr    FPEXC, r2\n    //pop {r2}\n    //vmsr    FPSCR, r2\n    vpop    {s16-s31}\n;#endif\n    // restore GPR and ret\n    pop {r0-r12,pc};\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "src/ExecBlock/CMakeLists.txt",
    "content": "if(QBDI_ARCH_X86_64 OR QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n\n# Add QBDI target\nset(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ExecBlock.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockManager.cpp\")\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/ExecBlock/Context.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef CONTEXT_H\n#define CONTEXT_H\n\n#include \"QBDI/Config.h\"\n\nnamespace QBDI {\n\nstruct HostState;\n\nstruct Context;\n\n} // namespace QBDI\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n#include \"ExecBlock/X86_64/Context_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"ExecBlock/ARM/Context_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"ExecBlock/AARCH64/Context_AARCH64.h\"\n#else\n#error \"No context for this architecture\"\n#endif\n\n#endif // CONTEXT_H\n"
  },
  {
    "path": "src/ExecBlock/ExecBlock.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <iterator>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string>\n#include <system_error>\n\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/Support/Error.h\"\n#include \"llvm/Support/Process.h\"\n\n#include \"devVariable.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/Context.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/ExecBlockFlags.h\"\n#include \"Patch/ExecBlockPatch.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/InstAnalysis_prive.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"QBDI/Options.h\"\n#include \"QBDI/PtrAuth.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nuint64_t ExecBlock::getPageSize() {\n  return llvm::expectedToOptional(llvm::sys::Process::getPageSize())\n      .value_or(4096);\n}\n\nExecBlock::ExecBlock(\n    const LLVMCPUs &llvmCPUs, VMInstanceRef vminstance,\n    const std::vector<std::unique_ptr<RelocatableInst>> *execBlockPrologue,\n    const std::vector<std::unique_ptr<RelocatableInst>> *execBlockEpilogue,\n    uint32_t epilogueSize_)\n    : vminstance(vminstance), llvmCPUs(llvmCPUs), epilogueSize(epilogueSize_),\n      isFull(false) {\n\n  // Allocate memory blocks\n  std::error_code ec;\n  uint64_t pageSize = getPageSize();\n  unsigned mflags = PF::MF_READ | PF::MF_WRITE;\n\n  if constexpr (is_ios) {\n    if (not isRWRXSupported()) {\n      QBDI_REQUIRE_ABORT(isRWXSupported(), \"allocation fail\");\n      mflags |= PF::MF_EXEC;\n    }\n  }\n\n  // Allocate 2 pages block\n  codeBlock = QBDI::allocateMappedMemory(2 * pageSize, nullptr, mflags, ec);\n  QBDI_REQUIRE_ABORT(codeBlock.base() != nullptr, \"allocation fail\");\n  QBDI_REQUIRE_ABORT(\n      codeBlock.base() == strip_ptrauth(codeBlock.base()),\n      \"Allocate a buffer with authenticated pointer, not supported\");\n  // Split it in two blocks\n  dataBlock = llvm::sys::MemoryBlock(\n      reinterpret_cast<void *>(reinterpret_cast<uint64_t>(codeBlock.base()) +\n                               pageSize),\n      pageSize);\n  codeBlock = llvm::sys::MemoryBlock(codeBlock.base(), pageSize);\n  QBDI_DEBUG(\"codeBlock @ 0x{:x} | dataBlock @ 0x{:x} | pageSize {} bytes\",\n             reinterpret_cast<rword>(codeBlock.base()),\n             reinterpret_cast<rword>(dataBlock.base()), pageSize);\n\n  // Other initializations\n  context = static_cast<Context *>(dataBlock.base());\n  shadows = reinterpret_cast<rword *>(\n      reinterpret_cast<rword>(dataBlock.base()) + sizeof(Context));\n  shadowIdx = 0;\n  currentSeq = 0;\n  currentInst = 0;\n  codeBlockPosition = 0;\n  codeBlockMaxSize = codeBlock.allocatedSize();\n  pageState = RW;\n\n  std::vector<std::unique_ptr<RelocatableInst>> execBlockPrologue_;\n  std::vector<std::unique_ptr<RelocatableInst>> execBlockEpilogue_;\n\n  const LLVMCPU &llvmcpu = llvmCPUs.getCPU(CPUMode::DEFAULT);\n\n  if (execBlockPrologue == nullptr) {\n    execBlockPrologue_ = getExecBlockPrologue(llvmcpu);\n    execBlockPrologue = &execBlockPrologue_;\n  }\n  if (execBlockEpilogue == nullptr) {\n    execBlockEpilogue_ = getExecBlockEpilogue(llvmcpu);\n    execBlockEpilogue = &execBlockEpilogue_;\n  }\n\n  if (epilogueSize == 0) {\n    for (const auto &inst : *execBlockEpilogue) {\n      epilogueSize += inst->getSize(llvmcpu);\n    }\n    QBDI_DEBUG(\"Detect Epilogue size: {}\", epilogueSize);\n  }\n  // JIT prologue and epilogue\n  codeBlockPosition = codeBlock.allocatedSize() - epilogueSize;\n  QBDI_REQUIRE_ABORT(applyRelocatedInst(*execBlockEpilogue, nullptr, llvmcpu),\n                     \"Fail to write Epilogue\");\n  QBDI_REQUIRE_ABORT(codeBlockPosition == codeBlock.allocatedSize(),\n                     \"Wrong Epilogue Size\");\n\n  codeBlockPosition = 0;\n  // forbid overwrite of the epilogue\n  codeBlockMaxSize = codeBlock.allocatedSize() - epilogueSize;\n\n  QBDI_REQUIRE_ABORT(applyRelocatedInst(*execBlockPrologue, nullptr, llvmcpu),\n                     \"Fail to write Prologue\");\n}\n\nExecBlock::~ExecBlock() {\n  // Reunite the 2 blocks before freeing them\n  codeBlock = llvm::sys::MemoryBlock(\n      codeBlock.base(), codeBlock.allocatedSize() + dataBlock.allocatedSize());\n  QBDI::releaseMappedMemory(codeBlock);\n}\n\nvoid ExecBlock::changeVMInstanceRef(VMInstanceRef vminstance) {\n  this->vminstance = vminstance;\n}\n\nvoid ExecBlock::show() const {\n  rword i;\n  uint64_t instSize;\n  bool dstatus;\n  llvm::ArrayRef<uint8_t> jitCode(\n      static_cast<const uint8_t *>(codeBlock.base()), codeBlockPosition);\n  size_t mode = 0;\n  const LLVMCPU *llvmcpu = &llvmCPUs.getCPU((CPUMode)mode);\n\n  fprintf(stderr, \"---- JIT CODE ----\\n\");\n  for (i = 0; i < jitCode.size(); i += instSize) {\n    llvm::MCInst inst;\n    std::string disass;\n\n    dstatus = llvmcpu->getInstruction(inst, instSize, jitCode.slice(i), i);\n    if constexpr (CPUMode::COUNT > 1) {\n      if (CPUMode::COUNT > 1 and not dstatus) { // Try to switch mode\n        mode = (mode + 1) % CPUMode::COUNT;\n        llvmcpu = &llvmCPUs.getCPU((CPUMode)mode);\n        dstatus = llvmcpu->getInstruction(inst, instSize, jitCode.slice(i), i);\n      }\n    }\n    QBDI_REQUIRE_ACTION(dstatus, break);\n\n    disass =\n        llvmcpu->showInst(inst, reinterpret_cast<rword>(codeBlock.base()) + i);\n    fprintf(stderr, \"%s\\n\", disass.c_str());\n  }\n\n  fprintf(stderr, \"---- CONTEXT ----\\n\");\n  for (i = 0; i < NUM_GPR; i++) {\n    fprintf(stderr, \"%s=0x%016\" PRIRWORD \" \",\n            llvmcpu->getRegisterName(GPR_ID[i]),\n            QBDI_GPR_GET(&context->gprState, i));\n    if (i % 4 == 0)\n      fprintf(stderr, \"\\n\");\n  }\n  fprintf(stderr, \"\\n\");\n\n  fprintf(stderr, \"---- SHADOWS ----\\n[\");\n  for (i = 0; i + 1 < shadowIdx; i++)\n    fprintf(stderr, \"0x%016\" PRIRWORD \", \", shadows[i]);\n  if (shadowIdx > 0)\n    fprintf(stderr, \"0x%016\" PRIRWORD, shadows[i - 1]);\n  fprintf(stderr, \"]\\n\");\n}\n\nVMAction ExecBlock::execute() {\n  QBDI_DEBUG(\"Executing ExecBlock 0x{:x} programmed with selector at 0x{:x}\",\n             reinterpret_cast<uintptr_t>(this), context->hostState.selector);\n\n  do {\n    context->hostState.callback = static_cast<rword>(0);\n    context->hostState.data = static_cast<rword>(0);\n\n    QBDI_DEBUG(\"Execution of ExecBlock 0x{:x} resumed at 0x{:x}\",\n               reinterpret_cast<uintptr_t>(this), context->hostState.selector);\n    run();\n\n    if (context->hostState.callback != 0) {\n      currentInst = context->hostState.origin;\n      rword currentPC = QBDI_GPR_GET(&context->gprState, REG_PC);\n\n      QBDI_DEBUG(\"Callback request by ExecBlock 0x{:x} for callback 0x{:x}\",\n                 reinterpret_cast<uintptr_t>(this),\n                 context->hostState.callback);\n      QBDI_REQUIRE(currentInst < instMetadata.size());\n\n      VMAction r =\n          (reinterpret_cast<InstCallback>(context->hostState.callback))(\n              vminstance, &context->gprState, &context->fprState,\n              (void *)context->hostState.data);\n\n      switch (r) {\n        case CONTINUE:\n          QBDI_DEBUG(\"Callback 0x{:x} returned CONTINUE\",\n                     context->hostState.callback);\n          if (QBDI_GPR_GET(&context->gprState, REG_PC) != currentPC) {\n            QBDI_WARN(\n                \"Callback returned CONTINUE but change PC: Ignore new value\");\n          }\n          break;\n        case SKIP_INST:\n          QBDI_DEBUG(\"Callback 0x{:x} returned SKIP_INST\",\n                     context->hostState.callback);\n          if (currentPC == instMetadata[currentInst].address) {\n            rword setPC = QBDI_GPR_GET(&context->gprState, REG_PC);\n            if (not instMetadata[currentInst].modifyPC) {\n              // warn only if PC is changed and isn't the endAddress\n              if (setPC != currentPC and\n                  setPC != instMetadata[currentInst].endAddress()) {\n                QBDI_WARN(\n                    \"Callback returned SKIP_INST but change PC: Ignore new \"\n                    \"value\");\n              }\n            } else {\n              // warn is PC is unchanged and equals to current instruction\n              // address\n              if (setPC == currentPC and\n                  setPC == instMetadata[currentInst].address) {\n                QBDI_WARN(\n                    \"Callback returned SKIP_INST without changing PC on \"\n                    \"instruction that should set it: this may create an \"\n                    \"infinite loop\");\n              }\n            }\n            context->hostState.selector =\n                reinterpret_cast<rword>(codeBlock.base()) +\n                static_cast<rword>(instRegistry[currentInst].offsetSkip);\n          } else {\n            QBDI_WARN(\n                \"POSTINST callback returned SKIP_INST: Use CONTINUE instead\");\n          }\n          break;\n        case SKIP_PATCH:\n          QBDI_DEBUG(\"Callback 0x{:x} returned SKIP_PATCH\",\n                     context->hostState.callback);\n          if (not instMetadata[currentInst].modifyPC and\n              QBDI_GPR_GET(&context->gprState, REG_PC) != currentPC) {\n            QBDI_WARN(\n                \"Callback returned SKIP_PATCH but change PC: Ignore new value\");\n          }\n          if (instMetadata[currentInst].modifyPC) {\n            QBDI_WARN(\n                \"Callback returned SKIP on instruction that change PC. Use \"\n                \"BREAK_TO_VM instead.\");\n            return BREAK_TO_VM;\n          } else if (currentInst == seqRegistry[currentSeq].endInstID) {\n            rword next_address = instMetadata[currentInst].address +\n                                 instMetadata[currentInst].instSize;\n#if defined(QBDI_ARCH_ARM)\n            if (instMetadata[currentInst].cpuMode == CPUMode::Thumb) {\n              next_address |= 1;\n            }\n#endif\n            QBDI_GPR_SET(&context->gprState, REG_PC, next_address);\n            return BREAK_TO_VM;\n          } else {\n            // go to currentInst + 1\n            currentInst += 1;\n            context->hostState.selector =\n                reinterpret_cast<rword>(codeBlock.base()) +\n                static_cast<rword>(instRegistry[currentInst].offset);\n          }\n          break;\n        case BREAK_TO_VM:\n          QBDI_DEBUG(\"Callback 0x{:x} returned BREAK_TO_VM\",\n                     context->hostState.callback);\n          return BREAK_TO_VM;\n        case STOP:\n          QBDI_DEBUG(\"Callback 0x{:x} returned STOP\",\n                     context->hostState.callback);\n          return STOP;\n      }\n    }\n  } while (context->hostState.callback != 0);\n  currentInst = seqRegistry[currentSeq].endInstID;\n\n  return CONTINUE;\n}\n\nbool ExecBlock::writeCodeByte(const llvm::ArrayRef<char> &array) {\n  QBDI_REQUIRE_ABORT(codeBlockPosition <= codeBlockMaxSize,\n                     \"Invalid position in codeBlock\");\n  if (array.size() > codeBlockMaxSize - codeBlockPosition) {\n    QBDI_DEBUG(\n        \"Fail to write buffer in codeBlock : \"\n        \"(array size: {}, remaining size: {})\",\n        array.size(), codeBlockMaxSize - codeBlockPosition);\n    return false;\n  }\n\n  memcpy(static_cast<char *>(codeBlock.base()) + codeBlockPosition,\n         array.data(), array.size());\n  codeBlockPosition += array.size();\n  return true;\n}\n\nbool ExecBlock::applyRelocatedInst(\n    const std::vector<std::unique_ptr<RelocatableInst>> &reloc,\n    std::vector<TagInfo> *tags, const LLVMCPU &llvmcpu, unsigned limit) {\n\n  for (const RelocatableInst::UniquePtr &inst : reloc) {\n    if (inst->getTag() != RelocatableInstTag::RelocInst) {\n      QBDI_DEBUG(\"RelocTag 0x{:x}\", inst->getTag());\n      if (tags != nullptr) {\n        tags->push_back(TagInfo{static_cast<uint16_t>(inst->getTag()),\n                                static_cast<uint16_t>(codeBlockPosition)});\n      }\n      continue;\n    } else {\n      llvm::SmallVector<char, 16> stream;\n      llvmcpu.writeInstruction(inst->reloc(this, llvmcpu), stream,\n                               getCurrentPC());\n\n#ifdef CHECK_INSTRUCTION_SIZE\n      unsigned instSize = inst->getSize(llvmcpu);\n      if (stream.size() != instSize) {\n        QBDI_ABORT(\n            \"getSize doesn't return the good size \"\n            \"(result: {}, expected: {})\",\n            instSize, stream.size());\n      }\n#endif\n\n      if ((codeBlockMaxSize - codeBlockPosition < stream.size() + limit) or\n          (not writeCodeByte(stream))) {\n        QBDI_DEBUG(\"Not enough space left: rollback\");\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nSeqWriteResult\nExecBlock::writeSequence(std::vector<Patch>::const_iterator seqIt,\n                         std::vector<Patch>::const_iterator seqEnd) {\n  unsigned startOffset = codeBlockPosition;\n  uint16_t startInstID = getNextInstID();\n  uint16_t seqID = getNextSeqID();\n  uint8_t executeFlags = 0;\n  unsigned patchWritten = 0;\n\n  // Check if there's enough space left\n  if (isFull) {\n    QBDI_DEBUG(\"ExecBlock 0x{:x} is full\", reinterpret_cast<uintptr_t>(this));\n    return {EXEC_BLOCK_FULL, 0, 0};\n  }\n\n  // Refuse to write empty sequence\n  if (seqIt == seqEnd) {\n    QBDI_WARN(\"Attempting to write empty sequence\");\n    return {EXEC_BLOCK_FULL, 0, 0};\n  }\n\n  CPUMode cpuMode = seqIt->metadata.cpuMode;\n  const LLVMCPU &llvmcpu = llvmCPUs.getCPU(cpuMode);\n\n  QBDI_DEBUG(\"Attempting to write {} patches to ExecBlock 0x{:x}\",\n             std::distance(seqIt, seqEnd), reinterpret_cast<uintptr_t>(this));\n  // Pages are RWX on iOS\n  // Ensure code block is RW\n  if constexpr (not is_ios) {\n    makeRW();\n  } else if (isRWRXSupported()) {\n    makeRW();\n  }\n  initScratchRegisterForPatch(seqIt, seqEnd);\n  bool needTerminator = true;\n  // JIT the basic block instructions patch per patch\n  // A patch correspond to an original instruction and should be written in its\n  // entierty\n  while (seqIt != seqEnd) {\n    unsigned rollbackOffset = codeBlockPosition;\n    uint32_t rollbackShadowIdx = shadowIdx;\n    size_t rollbackShadowRegistry = shadowRegistry.size();\n    size_t rollbackTagRegistry = tagRegistry.size();\n\n    QBDI_DEBUG_BLOCK({\n      std::string disass =\n          llvmcpu.showInst(seqIt->metadata.inst, seqIt->metadata.address);\n      QBDI_DEBUG(\n          \"Attempting to write patch of {} RelocatableInst to ExecBlock 0x{:x} \"\n          \"for instruction {:x}: {}\",\n          seqIt->metadata.patchSize, reinterpret_cast<uintptr_t>(this),\n          seqIt->metadata.address, disass.c_str());\n    });\n\n    // Attempt to write a complete patch. If not, rollback to the last complete\n    // patch written\n    if (not writePatch(seqIt, seqEnd, llvmcpu)) {\n\n      QBDI_DEBUG(\"Rolling back to offset 0x{:x}\", rollbackOffset);\n\n      QBDI_REQUIRE_ABORT(codeBlockPosition <=\n                             codeBlock.allocatedSize() - epilogueSize,\n                         \"Internal Error, Overflow in Epilogue\");\n      // Seek to the last complete patch written and terminate it with a\n      // terminator\n      codeBlockPosition = rollbackOffset;\n      // free shadows and tag allocated by the rollbacked code\n      shadowIdx = rollbackShadowIdx;\n      shadowRegistry.resize(rollbackShadowRegistry);\n      tagRegistry.resize(rollbackTagRegistry);\n      // It's a NULL rollback, don't terminate it\n      if (rollbackOffset == startOffset) {\n        QBDI_DEBUG(\"NULL rollback, nothing written to ExecBlock 0x{:x}\",\n                   reinterpret_cast<uintptr_t>(this));\n        return {EXEC_BLOCK_FULL, 0, 0};\n      }\n      needTerminator = true;\n      break;\n    } else {\n      // Complete instruction was written, we add the metadata\n      // Move the analysis of the instruction in the cached metadata\n      instMetadata.push_back(seqIt->metadata.lightCopy());\n      instMetadata.back().analysis.reset(seqIt->metadata.analysis.release());\n      // Register instruction\n      instRegistry.push_back(InstInfo{\n          seqID, 0, 0, static_cast<uint16_t>(rollbackShadowRegistry),\n          static_cast<uint16_t>(shadowRegistry.size() - rollbackShadowRegistry),\n          static_cast<uint16_t>(rollbackTagRegistry),\n          static_cast<uint16_t>(tagRegistry.size() - rollbackTagRegistry)});\n      // compute begin of the new instruction (writePatch can add extra data\n      // to perform the transition from the previous instruction, and we should\n      // skip it)\n      std::vector<TagInfo> beginPatchTag =\n          queryTagByInst(instRegistry.size() - 1, RelocTagPatchBegin);\n      QBDI_REQUIRE_ABORT(beginPatchTag.size() == 1,\n                         \"Internal Error: begin tag not found\");\n      instRegistry.back().offset = beginPatchTag[0].offset;\n      // compute offsetSkip of the new instruction\n      std::vector<TagInfo> endInstPatchTag =\n          queryTagByInst(instRegistry.size() - 1, RelocTagPatchInstEnd);\n      QBDI_REQUIRE_ABORT(endInstPatchTag.size() == 1,\n                         \"Internal Error: end tag not found\");\n      instRegistry.back().offsetSkip = endInstPatchTag[0].offset;\n      // set the scratch Register if needed\n      finalizeScratchRegisterForPatch();\n      // Update indexes\n      needTerminator = not seqIt->metadata.modifyPC;\n      executeFlags |= seqIt->metadata.execblockFlags;\n      seqIt++;\n      patchWritten += 1;\n    }\n  }\n  // The last instruction of the sequence doesn't end with a change of RIP/PC,\n  // add a Terminator\n  if (needTerminator) {\n    QBDI_DEBUG(\n        \"Writting terminator to ExecBlock 0x{:x} to finish non-exit sequence\",\n        reinterpret_cast<uintptr_t>(this));\n    RelocatableInst::UniquePtrVec terminator =\n        getTerminator(llvmcpu, instMetadata.back().endAddress());\n    QBDI_REQUIRE_ABORT(applyRelocatedInst(terminator, nullptr, llvmcpu),\n                       \"Fail to write Terminator\");\n  }\n  // JIT the jump to epilogue\n  RelocatableInst::UniquePtrVec jmpEpilogue = JmpEpilogue().genReloc(llvmcpu);\n  QBDI_REQUIRE_ABORT(applyRelocatedInst(jmpEpilogue, nullptr, llvmcpu),\n                     \"Fail to write jmpEpilogue\");\n  // change the flag of the basicblock\n  if (llvmcpu.hasOptions(Options::OPT_DISABLE_FPR)) {\n    executeFlags = 0;\n  } else if (llvmcpu.hasOptions(Options::OPT_DISABLE_OPTIONAL_FPR)) {\n    executeFlags = defaultExecuteFlags;\n  }\n  // Register sequence\n  uint16_t endInstID = getNextInstID() - 1;\n  seqRegistry.push_back(SeqInfo{startInstID, endInstID, executeFlags, cpuMode,\n                                instRegistry[startInstID].sr});\n  // Return write results\n  unsigned bytesWritten = codeBlockPosition - startOffset;\n  QBDI_REQUIRE_ABORT(codeBlockPosition <=\n                         codeBlock.allocatedSize() - epilogueSize,\n                     \"Internal Error, Overflow in Epilogue\");\n  QBDI_DEBUG(\"End write sequence in basicblock 0x{:x} with execFlags : {:x}\",\n             reinterpret_cast<uintptr_t>(this), executeFlags);\n  return SeqWriteResult{seqID, bytesWritten, patchWritten};\n}\n\nuint16_t ExecBlock::splitSequence(uint16_t instID) {\n  QBDI_REQUIRE(instID < instRegistry.size());\n  uint16_t seqID = instRegistry[instID].seqID;\n  seqRegistry.push_back(SeqInfo{\n      instID, seqRegistry[seqID].endInstID, seqRegistry[seqID].executeFlags,\n      seqRegistry[seqID].cpuMode, instRegistry[instID].sr});\n  return getNextSeqID() - 1;\n}\n\nvoid ExecBlock::makeRX() {\n  if (not isRX()) {\n    QBDI_DEBUG(\"Making ExecBlock 0x{:x} RX\", reinterpret_cast<uintptr_t>(this));\n    QBDI_REQUIRE_ABORT(!llvm::sys::Memory::protectMappedMemory(\n                           codeBlock, PF::MF_READ | PF::MF_EXEC),\n                       \"Fail to set the page permission to RX\");\n    pageState = RX;\n  }\n}\n\nvoid ExecBlock::makeRW() {\n  if (not isRW()) {\n    QBDI_DEBUG(\"Making ExecBlock 0x{:x} RW\", reinterpret_cast<uintptr_t>(this));\n    QBDI_REQUIRE_ABORT(!llvm::sys::Memory::protectMappedMemory(\n                           codeBlock, PF::MF_READ | PF::MF_WRITE),\n                       \"Fail to set the page permission to RW\");\n    pageState = RW;\n  }\n}\n\nuint16_t ExecBlock::newShadow(uint16_t tag) {\n  uint16_t id = shadowIdx++;\n  QBDI_REQUIRE_ABORT(id * sizeof(rword) <\n                         dataBlock.allocatedSize() - sizeof(Context),\n                     \"Shadow allocation fail\");\n  if (tag != ShadowReservedTag::Untagged) {\n    QBDI_DEBUG(\"Registering new tagged shadow {} for instID {} wih tag {:x}\",\n               id, getNextInstID(), tag);\n    shadowRegistry.push_back({getNextInstID(), tag, id});\n  }\n  return id;\n}\n\nuint16_t ExecBlock::getLastShadow(uint16_t tag) {\n  uint16_t nextInstID = getNextInstID();\n\n  for (auto it = shadowRegistry.crbegin(); it != shadowRegistry.crend(); ++it) {\n    if (it->instID == nextInstID && it->tag == tag) {\n      return it->shadowID;\n    }\n  }\n  QBDI_ABORT(\"Cannot found shadow tag {:x} for the current instruction\", tag);\n}\n\nvoid ExecBlock::setShadow(uint16_t id, rword v) {\n  QBDI_REQUIRE_ABORT(id * sizeof(rword) <\n                         dataBlock.allocatedSize() - sizeof(Context),\n                     \"Invalid shadow ID\");\n  QBDI_DEBUG(\"Set shadow {} to 0x{:x}\", id, v);\n  shadows[id] = v;\n}\n\nrword ExecBlock::getShadow(uint16_t id) const {\n  QBDI_REQUIRE_ABORT(id * sizeof(rword) <\n                         dataBlock.allocatedSize() - sizeof(Context),\n                     \"Invalid shadow ID\");\n  return shadows[id];\n}\n\nrword ExecBlock::getShadowOffset(uint16_t id) const {\n  rword offset = sizeof(Context) + id * sizeof(rword);\n  QBDI_REQUIRE_ABORT(offset < dataBlock.allocatedSize(), \"Invalid shadow ID\");\n  return offset;\n}\n\nuint16_t ExecBlock::getInstID(rword address, CPUMode cpuMode) const {\n  for (size_t i = 0; i < instMetadata.size(); i++) {\n    if (instMetadata[i].address == address and\n        instMetadata[i].cpuMode == cpuMode) {\n      return (uint16_t)i;\n    }\n  }\n  return NOT_FOUND;\n}\n\nconst InstMetadata &ExecBlock::getInstMetadata(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instMetadata.size());\n  return instMetadata[instID];\n}\n\nrword ExecBlock::getInstAddress(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instMetadata.size());\n  return instMetadata[instID].address;\n}\n\nrword ExecBlock::getInstInstrumentedAddress(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instMetadata.size());\n  return reinterpret_cast<rword>(codeBlock.base()) +\n         static_cast<rword>(instRegistry[instID].offset);\n}\n\nconst llvm::MCInst &ExecBlock::getOriginalMCInst(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instMetadata.size());\n  return instMetadata[instID].inst;\n}\n\nconst InstAnalysis *ExecBlock::getInstAnalysis(uint16_t instID,\n                                               AnalysisType type) const {\n  QBDI_REQUIRE(instID < instMetadata.size());\n  QBDI_REQUIRE(instID < instRegistry.size());\n  InstAnalysis *ana =\n      analyzeInstMetadata(instMetadata[instID], type,\n                          llvmCPUs.getCPU(instMetadata[instID].cpuMode));\n\n  // perform ANALYSIS_JIT if needed\n  if ((type & ANALYSIS_JIT) != 0 and (ana->analysisType & ANALYSIS_JIT) == 0) {\n    ana->analysisType |= ANALYSIS_JIT;\n    ana->patchAddress = getBaseCodeBlock() + instRegistry[instID].offset;\n    if (instRegistry.size() == instID + 1) {\n      ana->patchSize = codeBlockPosition - instRegistry[instID].offset;\n    } else {\n      ana->patchSize =\n          instRegistry[instID + 1].offset - instRegistry[instID].offset;\n    }\n    std::vector<TagInfo> beginInstPatchTag =\n        queryTagByInst(instID, RelocTagPatchInstBegin);\n    QBDI_REQUIRE_ABORT(beginInstPatchTag.size() == 1,\n                       \"Internal Error: begin tag not found\");\n    QBDI_REQUIRE_ABORT(beginInstPatchTag[0].offset >=\n                           instRegistry[instID].offset,\n                       \"Internal Error: invalid begin tag\");\n    ana->patchInstOffset =\n        beginInstPatchTag[0].offset - instRegistry[instID].offset;\n    ana->patchInstSize =\n        instRegistry[instID].offsetSkip - beginInstPatchTag[0].offset;\n  }\n\n  return ana;\n}\n\nuint16_t ExecBlock::getSeqID(rword address, CPUMode cpuMode) const {\n  for (size_t i = 0; i < seqRegistry.size(); i++) {\n    if (instMetadata[seqRegistry[i].startInstID].address == address and\n        instMetadata[seqRegistry[i].startInstID].cpuMode == cpuMode) {\n      return (uint16_t)i;\n    }\n  }\n  return NOT_FOUND;\n}\n\nuint16_t ExecBlock::getSeqID(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instRegistry.size());\n  return instRegistry[instID].seqID;\n}\n\nuint16_t ExecBlock::getSeqStart(uint16_t seqID) const {\n  QBDI_REQUIRE(seqID < seqRegistry.size());\n  return seqRegistry[seqID].startInstID;\n}\n\nuint16_t ExecBlock::getSeqEnd(uint16_t seqID) const {\n  QBDI_REQUIRE(seqID < seqRegistry.size());\n  return seqRegistry[seqID].endInstID;\n}\n\nconst llvm::ArrayRef<ShadowInfo>\nExecBlock::getShadowByInst(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instRegistry.size());\n  QBDI_REQUIRE(instRegistry[instID].shadowOffset <= shadowRegistry.size());\n  QBDI_REQUIRE(instRegistry[instID].shadowOffset +\n                   instRegistry[instID].shadowSize <=\n               shadowRegistry.size());\n\n  if (instRegistry[instID].shadowOffset == shadowRegistry.size()) {\n    return llvm::ArrayRef<ShadowInfo>{};\n  } else {\n    return llvm::ArrayRef<ShadowInfo>{\n        &shadowRegistry[instRegistry[instID].shadowOffset],\n        instRegistry[instID].shadowSize};\n  }\n}\n\nstd::vector<ShadowInfo> ExecBlock::queryShadowByInst(uint16_t instID,\n                                                     uint16_t tag) const {\n  std::vector<ShadowInfo> result;\n\n  for (const auto &reg : shadowRegistry) {\n    if ((instID == ANY || reg.instID == instID) &&\n        (tag == ANY || reg.tag == tag)) {\n      result.push_back(reg);\n    }\n  }\n\n  return result;\n}\n\nstd::vector<ShadowInfo> ExecBlock::queryShadowBySeq(uint16_t seqID,\n                                                    uint16_t tag) const {\n  std::vector<ShadowInfo> result;\n\n  if (seqID == ANY) {\n    for (const auto &reg : shadowRegistry) {\n      if (tag == ANY || reg.tag == tag) {\n        result.push_back(reg);\n      }\n    }\n  } else {\n    uint16_t firstInstID = getSeqStart(seqID);\n    uint16_t lastInstID = getSeqEnd(seqID);\n    for (const auto &reg : shadowRegistry) {\n      if (firstInstID <= reg.instID && reg.instID <= lastInstID &&\n          (tag == ANY || reg.tag == tag)) {\n        result.push_back(reg);\n      }\n    }\n  }\n\n  return result;\n}\n\nconst llvm::ArrayRef<TagInfo> ExecBlock::getTagByInst(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instRegistry.size());\n  QBDI_REQUIRE(instRegistry[instID].tagOffset <= tagRegistry.size());\n  QBDI_REQUIRE(instRegistry[instID].tagOffset + instRegistry[instID].tagSize <=\n               tagRegistry.size());\n\n  if (instRegistry[instID].tagOffset == tagRegistry.size()) {\n    return llvm::ArrayRef<TagInfo>{};\n  } else {\n    return llvm::ArrayRef<TagInfo>{&tagRegistry[instRegistry[instID].tagOffset],\n                                   instRegistry[instID].tagSize};\n  }\n}\n\nstd::vector<TagInfo> ExecBlock::queryTagByInst(uint16_t instID,\n                                               uint16_t tag) const {\n  std::vector<TagInfo> result;\n\n  for (const auto &reg : getTagByInst(instID)) {\n    if (reg.tag == tag) {\n      result.push_back(reg);\n    }\n  }\n\n  return result;\n}\n\nfloat ExecBlock::occupationRatio() const {\n  return static_cast<float>(codeBlock.allocatedSize() - getEpilogueOffset()) /\n         static_cast<float>(codeBlock.allocatedSize());\n}\n\nconst LLVMCPU &ExecBlock::getLLVMCPUByInst(uint16_t instID) const {\n  QBDI_REQUIRE(instID < instRegistry.size());\n  return llvmCPUs.getCPU(instMetadata[instID].cpuMode);\n}\n\nuint16_t ExecBlock::getPatchAddressOfJit(rword address) const {\n  if (address >= getCurrentPC()) {\n    // the address is after the last patch of this execblock. This may be an\n    // address outside of this Execblock, or in the epilogue\n    return NOT_FOUND;\n  }\n  if (instRegistry.size() <= 0) {\n    return NOT_FOUND;\n  }\n  if (address < getBaseCodeBlock() + instRegistry[0].offset) {\n    // the address is before the first patch of this execblock. This may be an\n    // address outside of this Execblock, or in the prologue\n    return NOT_FOUND;\n  }\n  uint16_t targetOffset = static_cast<uint16_t>(address - getBaseCodeBlock());\n\n  auto it =\n      std::lower_bound(instRegistry.cbegin(), instRegistry.cend(), targetOffset,\n                       [](const InstInfo &info, uint16_t value) {\n                         return info.offset <= value;\n                       });\n\n  if (it == instRegistry.cend()) {\n    return instRegistry.size() - 1;\n  } else {\n    return std::distance(instRegistry.cbegin(), it) - 1;\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBlock/ExecBlock.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef EXECBLOCK_H\n#define EXECBLOCK_H\n\n#include <memory>\n#include <stdint.h>\n#include <vector>\n\n#include \"llvm/ADT/ArrayRef.h\"\n#include \"llvm/Support/Memory.h\"\n\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/Types.h\"\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/State.h\"\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n#include \"ExecBlock/X86_64/ScratchRegisterInfo_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"ExecBlock/ARM/ScratchRegisterInfo_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"ExecBlock/AARCH64/ScratchRegisterInfo_AARCH64.h\"\n#else\n#error \"No ScratchRegisterInfo for this architecture\"\n#endif\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\n\nclass LLVMCPUs;\nclass LLVMCPU;\nclass RelocatableInst;\nclass Patch;\n\nstruct Context;\n\nstruct InstInfo {\n  uint16_t seqID;\n  uint16_t offset;\n  uint16_t offsetSkip;\n  uint16_t shadowOffset;\n  uint16_t shadowSize;\n  uint16_t tagOffset;\n  uint16_t tagSize;\n  ScratchRegisterSeqInfo sr;\n};\n\nstruct SeqInfo {\n  uint16_t startInstID;\n  uint16_t endInstID;\n  uint8_t executeFlags;\n  CPUMode cpuMode;\n  ScratchRegisterSeqInfo sr;\n};\n\nstruct SeqWriteResult {\n  uint16_t seqID;\n  unsigned bytesWritten;\n  unsigned patchWritten;\n};\n\nstruct ShadowInfo {\n  uint16_t instID;\n  uint16_t tag;\n  uint16_t shadowID;\n};\n\nstruct TagInfo {\n  uint16_t tag;\n  uint16_t offset;\n};\n\nstatic const uint16_t EXEC_BLOCK_FULL = 0xFFFF;\n\n/*! Manages the concept of an exec block made of two contiguous memory blocks\n * (one for the code, the other for the data) used to store and execute\n * instrumented basic blocks.\n */\nclass ExecBlock {\nprivate:\n  using PF = llvm::sys::Memory::ProtectionFlags;\n\n  enum PageState { RX, RW };\n\n  VMInstanceRef vminstance;\n  llvm::sys::MemoryBlock codeBlock;\n  llvm::sys::MemoryBlock dataBlock;\n  unsigned codeBlockPosition;\n  unsigned codeBlockMaxSize;\n  const LLVMCPUs &llvmCPUs;\n  Context *context;\n  rword *shadows;\n  std::vector<ShadowInfo> shadowRegistry;\n  std::vector<TagInfo> tagRegistry;\n  uint16_t shadowIdx;\n  std::vector<InstMetadata> instMetadata;\n  std::vector<InstInfo> instRegistry;\n  std::vector<SeqInfo> seqRegistry;\n  PageState pageState;\n  uint16_t currentSeq;\n  uint16_t currentInst;\n  uint32_t epilogueSize;\n  bool isFull;\n  ScratchRegisterInfo srInfo;\n\n  /*! Verify if the code block is in read execute mode.\n   *\n   * @return Return true if the code block is in read execute mode.\n   */\n  inline bool isRX() const { return pageState == RX; }\n\n  /*! Verify if the code block is in read write mode.\n   *\n   * @return Return true if the code block is in read write mode.\n   */\n  inline bool isRW() const { return pageState == RW; }\n\n  /*! Changes the code block permissions to RX.\n   */\n  void makeRX();\n\n  /*! Changes the code block permissions to RW.\n   */\n  void makeRW();\n\n  void initScratchRegisterForPatch(std::vector<Patch>::const_iterator seqStart,\n                                   std::vector<Patch>::const_iterator seqEnd);\n\n  bool writePatch(std::vector<Patch>::const_iterator seqCurrent,\n                  std::vector<Patch>::const_iterator seqEnd,\n                  const LLVMCPU &llvmcpu);\n\n  bool writeCodeByte(const llvm::ArrayRef<char> &);\n\n  bool\n  applyRelocatedInst(const std::vector<std::unique_ptr<RelocatableInst>> &reloc,\n                     std::vector<TagInfo> *tags, const LLVMCPU &llvmcpu,\n                     unsigned limit = 0);\n\n  void finalizeScratchRegisterForPatch();\n\npublic:\n  /*! Construct a new ExecBlock\n   *\n   * @param[in] llvmCPUs           LLVMCPU used to assemble instructions in the\n   *                               ExecBlock.\n   * @param[in] vminstance         Pointer to public engine interface\n   * @param[in] execBlockPrologue  cached prologue of ExecManager\n   * @param[in] execBlockEpilogue  cached epilogue of ExecManager\n   * @param[in] epilogueSize       size in bytes of the epilogue (0 is not know)\n   */\n  ExecBlock(\n      const LLVMCPUs &llvmCPUs, VMInstanceRef vminstance,\n      const std::vector<std::unique_ptr<RelocatableInst>> *execBlockPrologue =\n          nullptr,\n      const std::vector<std::unique_ptr<RelocatableInst>> *execBlockEpilogue =\n          nullptr,\n      uint32_t epilogueSize = 0);\n\n  ~ExecBlock();\n\n  ExecBlock(const ExecBlock &) = delete;\n  ExecBlock &operator=(const ExecBlock &) = delete;\n\n  /*! Change vminstance when VM object is moved\n   */\n  void changeVMInstanceRef(VMInstanceRef vminstance);\n\n  /*! Display the content of an exec block to stderr.\n   */\n  void show() const;\n\n  /* Low level run function. Does not take care of the callbacks.\n   */\n  void run();\n\n  static uint64_t getPageSize();\n\n  /*! Execute the sequence currently programmed in the selector of the exec\n   * block. Take care of the callbacks handling.\n   */\n  VMAction execute();\n\n  /*! Write a new sequence in the exec block. This function does not guarantee\n   * that the sequence will be written in its entierty and might stop before the\n   * end using an architecture specific terminator. Return 0 if the exec block\n   * was full and no instruction was written.\n   *\n   * @param seqStart [in] Iterator to the start of a list of patches.\n   * @param seqEnd   [in] Iterator to the end of a list of patches.\n   *\n   * @return A structure detailling the write operation result.\n   */\n  SeqWriteResult writeSequence(std::vector<Patch>::const_iterator seqStart,\n                               std::vector<Patch>::const_iterator seqEnd);\n\n  /*! Split an existing sequence at instruction instID to create a new sequence.\n   *\n   * @param instID  [in] ID of the instruction where to split the sequence at.\n   *\n   * @return The new sequence ID.\n   */\n  uint16_t splitSequence(uint16_t instID);\n\n  /*! Get the patch address associated with a Jitted Address.\n   *\n   * @param address  [in] The Jitted address. This is expected to point inside\n   *                      the codeBlock of this basicBlock\n   *\n   * @return The instruction ID that correspond to this JIT address, or\n   * NOT_FOUND.\n   */\n  uint16_t getPatchAddressOfJit(rword address) const;\n\n  /*! Get the address of the DataBlock\n   *\n   * @return The DataBlock offset.\n   */\n  rword getDataBlockBase() const {\n    return reinterpret_cast<rword>(dataBlock.base());\n  }\n\n  /*! Compute the offset between the current code stream position and the start\n   * of the data block. Used for pc relative memory access to the data block.\n   *\n   * @return The computed offset.\n   */\n  rword getDataBlockOffset() const {\n    return codeBlock.allocatedSize() - codeBlockPosition;\n  }\n\n  /*! Compute the offset between the current code stream position and the start\n   * of the exec block epilogue code. Used for computing the remaining code\n   * space left or jumping to the exec block epilogue at the end of a sequence.\n   *\n   * @return The computed offset.\n   */\n  rword getEpilogueOffset() const {\n    return codeBlock.allocatedSize() - epilogueSize - codeBlockPosition;\n  }\n\n  /*! Get the size of the epilogue\n   *\n   * @return The size of the epilogue.\n   */\n  uint32_t getEpilogueSize() const { return epilogueSize; }\n\n  /*! Obtain the value of the PC where the ExecBlock is currently writing\n   * instructions.\n   *\n   * @return The PC value.\n   */\n  rword getCurrentPC() const {\n    return reinterpret_cast<rword>(codeBlock.base()) + codeBlockPosition;\n  }\n\n  /*! Get the begining of the codeBlock\n   *\n   * @return The first address of the codeBlock\n   */\n  rword getBaseCodeBlock() const {\n    return reinterpret_cast<rword>(codeBlock.base());\n  }\n\n  /*! Obtain the current instruction ID.\n   *\n   * @return The current instruction ID.\n   */\n  uint16_t getNextInstID() const {\n    return static_cast<uint16_t>(instMetadata.size());\n  }\n\n  /*! Obtain the instruction ID for a specific address (the address must exactly\n   * match the start of the instruction).\n   *\n   * @param address The address of the start of the instruction.\n   * @param cpumode The mode of the instruction\n   *\n   * @return The instruction ID or NOT_FOUND.\n   */\n  uint16_t getInstID(rword address, CPUMode cpumode) const;\n\n  /*! Obtain the current instruction ID.\n   *\n   * @return The ID of the current instruction.\n   */\n  uint16_t getCurrentInstID() const { return currentInst; }\n\n  /*! Obtain the instruction metadata for a specific instruction ID.\n   *\n   * @param instID The instruction ID.\n   *\n   * @return The metadata of the instruction.\n   */\n  const InstMetadata &getInstMetadata(uint16_t instID) const;\n\n  /*! Obtain the instruction address for a specific instruction ID.\n   *\n   * @param instID The instruction ID.\n   *\n   * @return The real address of the instruction.\n   */\n  rword getInstAddress(uint16_t instID) const;\n\n  /*! Obtain the instrumented address for a specific instruction ID.\n   *\n   * @param instID The instruction ID.\n   *\n   * @return The address in the BasicBlock of the instruction.\n   */\n  rword getInstInstrumentedAddress(uint16_t instID) const;\n\n  /*! Obtain the original MCInst for a specific instruction ID.\n   *\n   * @param instID The instruction ID.\n   *\n   * @return The original MCInst of the instruction.\n   */\n  const llvm::MCInst &getOriginalMCInst(uint16_t instID) const;\n\n  /*! Obtain the analysis of an instruction. Analysis results are\n   * cached in the InstAnalysis. The validity of the returned pointer is only\n   * guaranteed until the end of the callback, else a deepcopy of the structure\n   * is required.\n   *\n   * @param[in] instID  The ID of the instruction to analyse.\n   * @param[in] type    Properties to retrieve during analysis.\n   *\n   * @return A InstAnalysis structure containing the analysis result.\n   */\n  const InstAnalysis *getInstAnalysis(uint16_t instID, AnalysisType type) const;\n\n  /*! Obtain the next sequence ID.\n   *\n   * @return The next sequence ID.\n   */\n  uint16_t getNextSeqID() const {\n    return static_cast<uint16_t>(seqRegistry.size());\n  }\n\n  /*! Obtain the sequence ID for a specific address (the address must exactly\n   * match the start of the sequence).\n   *\n   * @param address The address of the start of the sequence.\n   *\n   * @return The sequence ID or NOT_FOUND.\n   */\n  uint16_t getSeqID(rword address, CPUMode cpumode) const;\n\n  /*! Obtain the sequence ID containing a specific instruction ID.\n   *\n   * @param instID The instruction ID.\n   *\n   * @return The sequence ID or NOT_FOUND.\n   */\n  uint16_t getSeqID(uint16_t instID) const;\n\n  /*! Obtain the current sequence ID.\n   *\n   * @return The ID of the current sequence.\n   */\n  uint16_t getCurrentSeqID() const { return currentSeq; }\n\n  /*! Obtain the sequence start address for a specific sequence ID.\n   *\n   * @param seqID The sequence ID.\n   *\n   * @return The start address of the sequence.\n   */\n  uint16_t getSeqStart(uint16_t seqID) const;\n\n  /*! Obtain the instruction id of the sequence end address for a specific\n   * sequence ID.\n   *\n   * @param seqID The sequence ID.\n   *\n   * @return The end address of the sequence.\n   */\n  uint16_t getSeqEnd(uint16_t seqID) const;\n\n  /*! Set the selector of the exec block to a specific sequence offset. Used to\n   * program the execution of a specific sequence within the exec block.\n   *\n   *  @param seqID [in] Basic block ID within the exec block.\n   */\n  void selectSeq(uint16_t seqID);\n\n  /*! Get a pointer to the context structure stored in the data block.\n   *\n   * @return The context pointer.\n   */\n  Context *getContext() const { return context; }\n\n  /*! Allocate a new shadow within the data block. Used by relocation to load or\n   * store data from the instrumented code.\n   *\n   * @param tag The tag associated with the registration, 0xFFFF is reserved for\n   * unregistered shadows.\n   *\n   *  @return The shadow id (which is its index within the shadow array).\n   */\n  uint16_t newShadow(uint16_t tag = ShadowReservedTag::Untagged);\n\n  /*! Search the last Shadow with the tag for the current instruction.\n   *  Used by relocation to load or store data from the instrumented code.\n   *\n   *  @param tag The tag associated with the registration\n   *\n   *  @return The shadow id (which is its index within the shadow array).\n   */\n  uint16_t getLastShadow(uint16_t tag);\n\n  /*! Set the value of a shadow.\n   *\n   *  @param id [in] ID of the shadow to set.\n   *  @param v  [in] Value to assigne to the shadow.\n   */\n  void setShadow(uint16_t id, rword v);\n\n  /*! Get the value of a shadow.\n   *\n   *  @param id [in] ID of the shadow.\n   *\n   *  @return Value of the shadow.\n   */\n  rword getShadow(uint16_t id) const;\n\n  /*! Get the offset of a shadow within the data block.\n   *\n   *  @param id [in] ID of the shadow.\n   *\n   *  @return Offset of the shadow.\n   */\n  rword getShadowOffset(uint16_t id) const;\n\n  /* Get all registered shadows for an instruction\n   *\n   * @param instID  The id of the instruction in the ExecBlock\n   *\n   * @return a vector of shadowID matching the query\n   */\n  const llvm::ArrayRef<ShadowInfo> getShadowByInst(uint16_t instID) const;\n\n  /* Query registered shadows and returns a vector of matching shadowID\n   *\n   * @param\n   *\n   * @return a vector of shadowID matching the query\n   */\n  std::vector<ShadowInfo> queryShadowByInst(uint16_t instID,\n                                            uint16_t tag) const;\n\n  /* Query registered shadows and returns a vector of matching shadowID\n   *\n   * @param\n   *\n   * @return a vector of ShadowInfo matching the query\n   */\n  std::vector<ShadowInfo> queryShadowBySeq(uint16_t seqID, uint16_t tag) const;\n\n  /* Get all registered tag for an instruction\n   *\n   * @param instID  The id of the instruction in the ExecBlock\n   *\n   * @return a vector of tag matching the query\n   */\n  const llvm::ArrayRef<TagInfo> getTagByInst(uint16_t instID) const;\n\n  /* Query registered tag and returns a vector of matching tagID\n   *\n   * @param instID  The id of the instruction in the ExecBlock\n   * @param tag     The tag to search\n   *\n   * @return a vector of tagID matching the query\n   */\n  std::vector<TagInfo> queryTagByInst(uint16_t instID, uint16_t tag) const;\n\n  /* Get TagInfo JITTed address\n   *\n   * @param tinfo A TagInfo of this basicblock\n   *\n   * @return The address of the Tag in this ExecBlock\n   */\n  inline rword getAddressTag(const TagInfo &tinfo) const {\n    return reinterpret_cast<rword>(codeBlock.base()) + tinfo.offset;\n  }\n\n  /* Compute the occupation ratio of the ExecBlock.\n   *\n   * @return the occupation ratio.\n   */\n  float occupationRatio() const;\n\n  const ScratchRegisterInfo &getScratchRegisterInfo() const { return srInfo; }\n\n  /* Get the LLVMCPU used by an instruction\n   *\n   * @param instID  The id of the instruction in the ExecBlock\n   *\n   * @return the LLVMCPU for the instruction\n   */\n  const LLVMCPU &getLLVMCPUByInst(uint16_t instID) const;\n};\n\n} // namespace QBDI\n\n#endif // EXECBLOCK_H\n"
  },
  {
    "path": "src/ExecBlock/ExecBlockManager.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <iterator>\n#include <stdlib.h>\n#include <utility>\n\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"ExecBlock/ExecBlockManager.h\"\n#include \"ExecBroker/ExecBroker.h\"\n#include \"Patch/ExecBlockPatch.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nnamespace {\n\ninline rword getExecRegionKey(rword address, CPUMode cpumode) {\n  if constexpr (is_arm) {\n    if (cpumode != CPUMode::DEFAULT) {\n      address |= 1;\n    } else {\n      address &= (~1);\n    }\n  }\n  return address;\n}\n\n} // namespace\n\nExecBlockManager::ExecBlockManager(const LLVMCPUs &llvmCPUs,\n                                   VMInstanceRef vminstance)\n    : total_translated_size(1), total_translation_size(1),\n      vminstance(vminstance), llvmCPUs(llvmCPUs),\n      execBlockPrologue(\n          getExecBlockPrologue(llvmCPUs.getCPU(CPUMode::DEFAULT))),\n      execBlockEpilogue(\n          getExecBlockEpilogue(llvmCPUs.getCPU(CPUMode::DEFAULT))) {\n\n  auto execBrokerBlock = std::make_unique<ExecBlock>(\n      llvmCPUs, vminstance, &execBlockPrologue, &execBlockEpilogue, 0);\n  epilogueSize = execBrokerBlock->getEpilogueSize();\n  execBroker = std::make_unique<ExecBroker>(std::move(execBrokerBlock),\n                                            llvmCPUs, vminstance);\n}\n\nExecBlockManager::~ExecBlockManager() {\n  QBDI_DEBUG_BLOCK({ this->printCacheStatistics(); });\n  clearCache();\n}\n\nvoid ExecBlockManager::changeVMInstanceRef(VMInstanceRef vminstance) {\n  this->vminstance = vminstance;\n  execBroker->changeVMInstanceRef(vminstance);\n  for (auto &reg : regions) {\n    for (auto &block : reg.blocks) {\n      block->changeVMInstanceRef(vminstance);\n    }\n  }\n}\n\nfloat ExecBlockManager::getExpansionRatio() const {\n  QBDI_DEBUG(\"{} / {}\", total_translation_size, total_translated_size);\n  return static_cast<float>(total_translation_size) /\n         static_cast<float>(total_translated_size);\n}\n\nvoid ExecBlockManager::printCacheStatistics() const {QBDI_DEBUG_BLOCK({\n  float mean_occupation = 0.0;\n  size_t region_overflow = 0;\n  QBDI_DEBUG(\"\\tCache made of {} regions:\", regions.size());\n  for (size_t i = 0; i < regions.size(); i++) {\n    float occupation = 0.0;\n    for (size_t j = 0; j < regions[i].blocks.size(); j++) {\n      occupation += regions[i].blocks[j]->occupationRatio();\n    }\n    if (regions[i].blocks.size() > 1) {\n      region_overflow += 1;\n    }\n    if (regions[i].blocks.size() > 0) {\n      occupation /= regions[i].blocks.size();\n    }\n    mean_occupation += occupation;\n    QBDI_DEBUG(\"\\t\\t[0x{:x}, 0x{:x}]: {} blocks, {} occupation ratio\",\n               regions[i].covered.start(), regions[i].covered.end(),\n               regions[i].blocks.size(), occupation);\n  }\n  if (regions.size() > 0) {\n    mean_occupation /= regions.size();\n  }\n  QBDI_DEBUG(\"\\tMean occupation ratio: {}\", mean_occupation);\n  QBDI_DEBUG(\"\\tRegion overflow count: {}\", region_overflow);\n})}\n\nExecBlock *ExecBlockManager::getProgrammedExecBlock(rword address,\n                                                    CPUMode cpumode,\n                                                    SeqLoc *programmedSeqLock) {\n  QBDI_DEBUG(\"Looking up sequence at address {:x} mode {}\", address, cpumode);\n\n  size_t r = searchRegion(address);\n\n  if (r < regions.size() && regions[r].covered.contains(address)) {\n    ExecRegion &region = regions[r];\n    const auto target = getExecRegionKey(address, cpumode);\n\n    // Attempting sequenceCache resolution\n    const auto seqLoc = region.sequenceCache.find(target);\n    if (seqLoc != region.sequenceCache.end()) {\n      QBDI_DEBUG(\"Found sequence 0x{:x} ({}) in ExecBlock 0x{:x} as seqID {:x}\",\n                 address, cpumode,\n                 reinterpret_cast<uintptr_t>(\n                     region.blocks[seqLoc->second.blockIdx].get()),\n                 seqLoc->second.seqID);\n      // copy current sequence info\n      if (programmedSeqLock != nullptr) {\n        *programmedSeqLock = seqLoc->second;\n      }\n      // Select sequence and return execBlock\n      region.blocks[seqLoc->second.blockIdx]->selectSeq(seqLoc->second.seqID);\n      return region.blocks[seqLoc->second.blockIdx].get();\n    }\n\n    // Attempting instCache resolution\n    const auto instLoc = region.instCache.find(target);\n    if (instLoc != region.instCache.end()) {\n      // Retrieving corresponding block and seqLoc\n      ExecBlock *block = region.blocks[instLoc->second.blockIdx].get();\n      uint16_t existingSeqId = block->getSeqID(instLoc->second.instID);\n      const SeqLoc &existingSeqLoc = region.sequenceCache[getExecRegionKey(\n          block->getInstMetadata(block->getSeqStart(existingSeqId)).address,\n          cpumode)];\n      // Creating a new sequence at that instruction and\n      // saving it in the sequenceCache\n      uint16_t newSeqID = block->splitSequence(instLoc->second.instID);\n      region.sequenceCache[target] = SeqLoc{\n          instLoc->second.blockIdx, newSeqID, existingSeqLoc.bbEnd, address,\n          existingSeqLoc.seqEnd,\n      };\n      QBDI_DEBUG(\n          \"Splitted seqID {:x} at instID {:x} in ExecBlock 0x{:x} as new \"\n          \"sequence with seqID {:x}\",\n          existingSeqId, instLoc->second.instID,\n          reinterpret_cast<uintptr_t>(block), newSeqID);\n      // copy current sequence info\n      if (programmedSeqLock != nullptr) {\n        *programmedSeqLock = regions[r].sequenceCache[target];\n      }\n      block->selectSeq(newSeqID);\n      return block;\n    }\n  }\n  QBDI_DEBUG(\"Cache miss for sequence 0x{:x} ({})\", address, cpumode);\n  return nullptr;\n}\n\nconst ExecBlock *ExecBlockManager::getExecBlock(rword address,\n                                                CPUMode cpumode) const {\n  QBDI_DEBUG(\"Looking up address {:x} ({})\", address, cpumode);\n\n  size_t r = searchRegion(address);\n\n  if (r < regions.size() && regions[r].covered.contains(address)) {\n    const ExecRegion &region = regions[r];\n\n    // Attempting instCache resolution\n    const auto instLoc =\n        region.instCache.find(getExecRegionKey(address, cpumode));\n    if (instLoc != region.instCache.end()) {\n      QBDI_DEBUG(\"Found address 0x{:x} ({}) in ExecBlock 0x{:x}\", address,\n                 cpumode,\n                 reinterpret_cast<uintptr_t>(\n                     region.blocks[instLoc->second.blockIdx].get()));\n      return region.blocks[instLoc->second.blockIdx].get();\n    }\n  }\n  QBDI_DEBUG(\"Cache miss for address 0x{:x} ({})\", address, cpumode);\n  return nullptr;\n}\n\nsize_t\nExecBlockManager::preWriteBasicBlock(const std::vector<Patch> &basicBlock) {\n  // prereserve the region in the cache and return the instruction that are\n  // already in the cache for this basicBlock\n\n  // Locating an approriate cache region\n  const Range<rword> bbRange{basicBlock.front().metadata.address,\n                             basicBlock.back().metadata.endAddress(),\n                             real_addr_t()};\n  size_t r = findRegion(bbRange);\n  ExecRegion &region = regions[r];\n\n  // detect cached instruction\n  size_t patchEnd = basicBlock.size();\n\n  while (patchEnd > 0 && region.instCache.count(getExecRegionKey(\n                             basicBlock[patchEnd - 1].metadata.address,\n                             basicBlock[patchEnd - 1].metadata.cpuMode)) != 0) {\n    patchEnd--;\n  }\n\n  return patchEnd;\n}\n\nvoid ExecBlockManager::writeBasicBlock(std::vector<Patch> &&basicBlock,\n                                       size_t patchEnd) {\n  unsigned translated = 0;\n  unsigned translation = 0;\n  size_t patchIdx = 0;\n  const Patch &firstPatch = basicBlock.front();\n  const Patch &lastPatch = basicBlock.back();\n  rword bbStart = firstPatch.metadata.address;\n  rword bbEnd = lastPatch.metadata.endAddress();\n\n  // Locating an approriate cache region\n  const Range<rword> bbRange{bbStart, bbEnd, real_addr_t()};\n  size_t r = findRegion(bbRange);\n  ExecRegion &region = regions[r];\n\n  // Basic block truncation to prevent dedoubled instruction\n  // patchEnd must be the number of instruction that wasn't in the cache for\n  // this basicblock\n  QBDI_REQUIRE_ABORT(patchEnd <= basicBlock.size(), \"Internal error\");\n  QBDI_REQUIRE_ABORT(patchEnd == basicBlock.size() ||\n                         region.instCache.count(getExecRegionKey(\n                             basicBlock[patchEnd].metadata.address,\n                             basicBlock[patchEnd].metadata.cpuMode)) == 1,\n                     \"Internal error, basicBlock end not found in the cache\");\n\n  while (patchEnd > 0 && region.instCache.count(getExecRegionKey(\n                             basicBlock[patchEnd - 1].metadata.address,\n                             basicBlock[patchEnd - 1].metadata.cpuMode)) != 0) {\n    // should never happen if preWriteBasicBlock is used\n    patchEnd--;\n  }\n\n  // Cache integrity safeguard, should never happen\n  if (patchEnd == 0) {\n    QBDI_DEBUG(\"Cache hit, basic block 0x{:x} already exist\",\n               firstPatch.metadata.address);\n    return;\n  }\n  QBDI_DEBUG(\"Writting new basic block 0x{:x}\", firstPatch.metadata.address);\n\n  // Writing the basic block as one or more sequences\n  while (patchIdx < patchEnd) {\n    // Attempting to find an ExecBlock in the region\n    for (size_t i = 0; true; i++) {\n      // If the region doesn't have enough space in its ExecBlocks, we add one.\n      // Optimally, a region should only have one ExecBlocks but misspredictions\n      // or oversized basic blocks can cause overflows.\n      if (i >= region.blocks.size()) {\n        QBDI_REQUIRE_ABORT(i < (1 << 16),\n                           \"Too many ExecBlock in the same region\");\n        region.blocks.emplace_back(std::make_unique<ExecBlock>(\n            llvmCPUs, vminstance, &execBlockPrologue, &execBlockEpilogue,\n            epilogueSize));\n        codeBlockMap[region.blocks.back()->getBaseCodeBlock()] =\n            region.blocks.back().get();\n      }\n      // Write sequence\n      SeqWriteResult res = region.blocks[i]->writeSequence(\n          basicBlock.begin() + patchIdx, basicBlock.begin() + patchEnd);\n      // Successful write\n      if (res.seqID != EXEC_BLOCK_FULL) {\n        // Saving sequence in the sequence cache\n        regions[r].sequenceCache[getExecRegionKey(\n            basicBlock[patchIdx].metadata.address,\n            basicBlock[patchIdx].metadata.cpuMode)] = SeqLoc{\n            static_cast<uint16_t>(i),\n            res.seqID,\n            bbEnd,\n            basicBlock[patchIdx].metadata.address,\n            basicBlock[patchIdx + res.patchWritten - 1].metadata.endAddress(),\n        };\n        // Generate instruction mapping cache\n        uint16_t startID = region.blocks[i]->getSeqStart(res.seqID);\n        for (size_t j = 0; j < res.patchWritten; j++) {\n          region.instCache[getExecRegionKey(\n              basicBlock[patchIdx + j].metadata.address,\n              basicBlock[patchIdx + j].metadata.cpuMode)] = InstLoc{\n              static_cast<uint16_t>(i), static_cast<uint16_t>(startID + j)};\n          std::move(basicBlock[patchIdx + j].userInstCB.begin(),\n                    basicBlock[patchIdx + j].userInstCB.end(),\n                    std::back_inserter(region.userInstCB));\n          basicBlock[patchIdx + j].userInstCB.clear();\n        }\n        QBDI_DEBUG(\n            \"Sequence 0x{:x}-0x{:x} written in ExecBlock 0x{:x} as seqID {:x}\",\n            basicBlock[patchIdx].metadata.address,\n            basicBlock[patchIdx + res.patchWritten - 1].metadata.endAddress(),\n            reinterpret_cast<uintptr_t>(region.blocks[i].get()), res.seqID);\n        // Updating counters\n        translated +=\n            basicBlock[patchIdx + res.patchWritten - 1].metadata.endAddress() -\n            basicBlock[patchIdx].metadata.address;\n        translation += res.bytesWritten;\n        patchIdx += res.patchWritten;\n        break;\n      }\n    }\n  }\n  // Updating stats\n  total_translation_size += translation;\n  total_translated_size += translated;\n  updateRegionStat(r, translated);\n}\n\nsize_t ExecBlockManager::searchRegion(rword address) const {\n  size_t low = 0;\n  size_t high = regions.size();\n  if (regions.size() == 0) {\n    return 0;\n  }\n  QBDI_DEBUG(\"Searching for address 0x{:x}\", address);\n  // Binary search of the first region to look at\n  while (low + 1 != high) {\n    size_t idx = (low + high) / 2;\n    if (regions[idx].covered.start() > address) {\n      high = idx;\n    } else if (regions[idx].covered.end() <= address) {\n      low = idx;\n    } else {\n      QBDI_DEBUG(\"Exact match for region {} [0x{:x}, 0x{:x}]\", idx,\n                 regions[idx].covered.start(), regions[idx].covered.end());\n      return idx;\n    }\n  }\n  QBDI_DEBUG(\"Low match for region {} [0x{:x}, 0x{:x}]\", low,\n             regions[low].covered.start(), regions[low].covered.end());\n  return low;\n}\n\nvoid ExecBlockManager::mergeRegion(size_t i) {\n\n  QBDI_REQUIRE_ACTION(i + 1 < regions.size(), return);\n  QBDI_REQUIRE_ABORT(regions[i].blocks.size() + regions[i + 1].blocks.size() <\n                         (1 << 16),\n                     \"Too many ExecBlock in the same region\");\n\n  QBDI_DEBUG(\"Merge region {} [0x{:x}, 0x{:x}] and region {} [0x{:x}, 0x{:x}]\",\n             i, regions[i].covered.start(), regions[i].covered.end(), i + 1,\n             regions[i + 1].covered.start(), regions[i + 1].covered.end());\n  // SeqLoc\n  for (const auto &it : regions[i + 1].sequenceCache) {\n    regions[i].sequenceCache[it.first] = SeqLoc{\n        static_cast<uint16_t>(it.second.blockIdx + regions[i].blocks.size()),\n        it.second.seqID, it.second.bbEnd, it.second.seqStart, it.second.seqEnd};\n  }\n  // InstLoc\n  for (const auto &it : regions[i + 1].instCache) {\n    regions[i].instCache[it.first] = InstLoc{\n        static_cast<uint16_t>(it.second.blockIdx + regions[i].blocks.size()),\n        it.second.instID,\n    };\n  }\n\n  // range\n  regions[i].covered.setEnd(regions[i + 1].covered.end());\n\n  // ExecBlock\n  std::move(regions[i + 1].blocks.begin(), regions[i + 1].blocks.end(),\n            std::back_inserter(regions[i].blocks));\n  // flush\n  regions[i].toFlush |= regions[i + 1].toFlush;\n\n  regions.erase(regions.begin() + i + 1);\n}\n\nsize_t ExecBlockManager::findRegion(const Range<rword> &codeRange) {\n  size_t best_region = regions.size();\n  size_t low = searchRegion(codeRange.start());\n  unsigned best_cost = 0xFFFFFFFF;\n\n  // when low == 0, three cases:\n  //  - no regions => no loop\n  //  - codeRange.start is before the first region => 1 round of the loop to\n  //    determine if we create a region or create a new one\n  //  - codeRange.start is in or after the first region => 2 rounds to\n  //    determine if we create a region or use the first or the second one\n  size_t limit = 2;\n  if (low == 0 and regions.size() != 0 and\n      codeRange.start() < regions[0].covered.start())\n    limit = 1;\n\n  for (size_t i = low; i < low + limit && i < regions.size(); i++) {\n    unsigned cost = 0;\n    // Easy case: the code range is inside one of the region, we can return\n    // immediately\n    if (regions[i].covered.contains(codeRange)) {\n      QBDI_DEBUG(\n          \"Basic block [0x{:x}, 0x{:x}] assigned to region {} [0x{:x}, 0x{:x}]\",\n          codeRange.start(), codeRange.end(), i, regions[i].covered.start(),\n          regions[i].covered.end());\n      return i;\n    }\n\n    // Medium case: the code range overlaps.\n    // This case may append when instrument unaligned code\n    // In order to avoid two regions to overlaps,\n    //  we must use the first region that overlaps\n    if (regions[i].covered.overlaps(codeRange)) {\n      QBDI_DEBUG(\n          \"Region {} [0x{:x}, 0x{:x}] overlaps a part of basic block [0x{:x}, \"\n          \"0x{:x}], Try extend\",\n          i, regions[i].covered.start(), regions[i].covered.end(),\n          codeRange.start(), codeRange.end());\n      // part 1, codeRange.start() must be on the regions[i] to avoid error with\n      // searchRegion\n      if (codeRange.start() < regions[i].covered.start()) {\n        // the previous region mustn't containt codeRange.start\n        // (should never happend as the iteration test the previous block first)\n        QBDI_REQUIRE_ABORT(i == 0 or regions[i - 1].covered.end() <=\n                                         codeRange.start(),\n                           \"Internal Error\");\n        regions[i].covered.setStart(codeRange.start());\n      }\n\n      // part 2, codeRange.end() SHOULD be in the region.\n      if (regions[i].covered.end() < codeRange.end()) {\n        if (i + 1 == regions.size() or\n            codeRange.end() <= regions[i + 1].covered.start()) {\n          // extend the current region if no overlaps the next region\n          regions[i].covered.setEnd(codeRange.end());\n        } else {\n          // the range overlaps two region, merge them\n          mergeRegion(i);\n        }\n      }\n      QBDI_DEBUG(\"New Region {} [0x{:x}, 0x{:x}]\", i,\n                 regions[i].covered.start(), regions[i].covered.end());\n\n      return i;\n    }\n\n    // Hard case: it's in the available budget of one the region. Keep the\n    // lowest cost. First compute the required cost for the region to cover this\n    // extended range.\n    if (regions[i].covered.end() < codeRange.end()) {\n      cost += (codeRange.end() - regions[i].covered.end());\n    }\n    if (regions[i].covered.start() > codeRange.start()) {\n      cost += (regions[i].covered.start() - codeRange.start());\n    }\n    // Make sure that such cost is available and that it's better than previous\n    // candidates\n    if (cost < regions[i].available &&\n        (cost == 0 ||\n         getExpansionRatio() < static_cast<float>(regions[i].available) /\n                                   static_cast<float>(cost)) &&\n        cost < best_cost) {\n      best_cost = cost;\n      best_region = i;\n    }\n  }\n  // We found an extension candidate\n  if (best_region != regions.size()) {\n    QBDI_DEBUG(\n        \"Extending region {} [0x{:x}, 0x{:x}] to cover basic block [0x{:x}, \"\n        \"0x{:x}]\",\n        best_region, regions[best_region].covered.start(),\n        regions[best_region].covered.end(), codeRange.start(), codeRange.end());\n    if (regions[best_region].covered.end() < codeRange.end()) {\n      regions[best_region].covered.setEnd(codeRange.end());\n    }\n    if (regions[best_region].covered.start() > codeRange.start()) {\n      regions[best_region].covered.setStart(codeRange.start());\n    }\n    return best_region;\n  }\n  // Else we have to create a new region\n  // Find a place to insert it\n  size_t insert = low;\n  for (; insert < regions.size(); insert++) {\n    if (regions[insert].covered.start() > codeRange.start()) {\n      break;\n    }\n  }\n  QBDI_DEBUG(\"Creating new region {} to cover basic block [0x{:x}, 0x{:x}]\",\n             insert, codeRange.start(), codeRange.end());\n  regions.emplace(regions.begin() + insert, codeRange);\n  regionsReduceList.insertEnd(regions[insert]);\n  return insert;\n}\n\nvoid ExecBlockManager::updateRegionStat(size_t r, rword translated) {\n  regions[r].translated += translated;\n  // Remaining code block space\n  regions[r].available = regions[r].blocks[0]->getEpilogueOffset();\n  // Space which needs to be reserved for the non translated part of the covered\n  // region\n  unsigned reserved = static_cast<unsigned>(\n      (static_cast<float>(regions[r].covered.size() - regions[r].translated)) *\n      getExpansionRatio());\n  QBDI_DEBUG(\n      \"Region {} has {} bytes available of which {} are reserved for {} bytes \"\n      \"of untranslated code\",\n      r, regions[r].available, reserved,\n      (regions[r].covered.size() - regions[r].translated));\n  if (reserved > regions[r].available) {\n    regions[r].available = 0;\n  } else {\n    regions[r].available -= reserved;\n  }\n}\n\nvoid ExecBlockManager::clearCache(RangeSet<rword> rangeSet) {\n  const std::vector<Range<rword>> &ranges = rangeSet.getRanges();\n  for (Range<rword> r : ranges) {\n    clearCache(r);\n  }\n  // Probably comming from an instrumentation change, reset translation counters\n  total_translated_size = 1;\n  total_translation_size = 1;\n}\n\nvoid ExecBlockManager::flushCommit() {\n  // It needs to be erased from last to first to preserve index validity\n  if (needFlush) {\n    QBDI_DEBUG(\"Flushing analysis caches\");\n    auto delFunc = [&](const ExecRegion &r) -> bool {\n      if (r.toFlush) {\n        QBDI_DEBUG(\"Erasing region [0x{:x}, 0x{:x}]\", r.covered.start(),\n                   r.covered.end());\n        for (const auto &block : r.blocks) {\n          codeBlockMap.erase(block->getBaseCodeBlock());\n        }\n      }\n      return r.toFlush;\n    };\n\n    regions.erase(std::remove_if(regions.begin(), regions.end(), delFunc),\n                  regions.end());\n    needFlush = false;\n  }\n}\n\nvoid ExecBlockManager::clearCache(Range<rword> range) {\n  size_t i = 0;\n  QBDI_DEBUG(\"Erasing range [0x{:x}, 0x{:x}]\", range.start(), range.end());\n  for (i = 0; i < regions.size(); i++) {\n    if (regions[i].covered.overlaps(range)) {\n      regions[i].toFlush = true;\n      needFlush = true;\n    }\n  }\n}\n\nvoid ExecBlockManager::clearCache(bool flushNow) {\n  QBDI_DEBUG(\"Erasing all cache\");\n  if (flushNow) {\n    regions.clear();\n    total_translated_size = 1;\n    total_translation_size = 1;\n    needFlush = false;\n  } else {\n    for (auto &r : regions) {\n      r.toFlush = true;\n      needFlush = true;\n    }\n  }\n}\n\nvoid ExecBlockManager::reduceCacheTo(uint32_t nb) {\n  uint32_t nbBlock = getNbExecBlock();\n  if (nb >= nbBlock) {\n    return;\n  }\n  uint32_t target = nbBlock - nb;\n  needFlush = true;\n\n  for (auto &r : regionsReduceList) {\n    r.toFlush = true;\n\n    if (target <= r.blocks.size()) {\n      return;\n    }\n    target -= r.blocks.size();\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBlock/ExecBlockManager.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef EXECBLOCKMANAGER_H\n#define EXECBLOCKMANAGER_H\n\n#include <algorithm>\n#include <map>\n#include <memory>\n#include <stddef.h>\n#include <stdint.h>\n#include <vector>\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n\n#include \"Utility/MovableDoubleLinkedList.h\"\n\nnamespace QBDI {\n\nclass ExecBlock;\nclass ExecBroker;\nclass LLVMCPUs;\nclass Patch;\nclass RelocatableInst;\n\nstruct InstLoc {\n  uint16_t blockIdx;\n  uint16_t instID;\n};\n\nstruct SeqLoc {\n  uint16_t blockIdx;\n  uint16_t seqID;\n  rword bbEnd;\n  rword seqStart;\n  rword seqEnd;\n};\n\nclass ExecRegion : public MovableDoubleLinkedListElement<ExecRegion> {\npublic:\n  Range<rword> covered;\n  unsigned translated = 0;\n  unsigned available = 0;\n  std::vector<std::unique_ptr<ExecBlock>> blocks;\n  // Note for sequenceCache instCache\n  // The key must be generate with getExecRegionKey\n  std::map<rword, SeqLoc> sequenceCache;\n  std::map<rword, InstLoc> instCache;\n  bool toFlush = false;\n\n  // lambda ptr for user callback set with addInstrRule\n  // These pointers should be remove at the same time as the region\n  std::vector<std::unique_ptr<InstCbLambda>> userInstCB;\n\n  ExecRegion(const Range<rword> &c)\n      : MovableDoubleLinkedListElement<ExecRegion>(), covered(c) {}\n\n  ExecRegion(ExecRegion &&o) = default;\n  ExecRegion &operator=(ExecRegion &&o) = default;\n};\n\nclass ExecBlockManager {\nprivate:\n  std::unique_ptr<ExecBroker> execBroker;\n  std::vector<ExecRegion> regions;\n  std::map<rword, ExecBlock *> codeBlockMap;\n  MovableDoubleLinkedList<ExecRegion> regionsReduceList;\n  rword total_translated_size;\n  rword total_translation_size;\n  bool needFlush;\n\n  VMInstanceRef vminstance;\n  const LLVMCPUs &llvmCPUs;\n\n  // cache ExecBlock prologue and epilogue\n  uint32_t epilogueSize;\n  const std::vector<std::unique_ptr<RelocatableInst>> execBlockPrologue;\n  const std::vector<std::unique_ptr<RelocatableInst>> execBlockEpilogue;\n\n  size_t searchRegion(rword start) const;\n\n  void mergeRegion(size_t i);\n\n  size_t findRegion(const Range<rword> &codeRange);\n\n  void updateRegionStat(size_t r, rword translated);\n\n  float getExpansionRatio() const;\n\npublic:\n  ExecBlockManager(const LLVMCPUs &llvmCPUs, VMInstanceRef vminstance);\n\n  ~ExecBlockManager();\n\n  ExecBlockManager(const ExecBlockManager &) = delete;\n\n  void changeVMInstanceRef(VMInstanceRef vminstance);\n\n  inline ExecBroker *getExecBroker() { return execBroker.get(); }\n\n  void printCacheStatistics() const;\n\n  ExecBlock *getProgrammedExecBlock(rword address, CPUMode cpumode,\n                                    SeqLoc *programmedSeqLock = nullptr);\n\n  const ExecBlock *getExecBlock(rword address, CPUMode cpumode) const;\n\n  size_t preWriteBasicBlock(const std::vector<Patch> &basicBlock);\n\n  void writeBasicBlock(std::vector<Patch> &&basicBlock, size_t patchEnd);\n\n  bool isFlushPending() { return needFlush; }\n\n  void flushCommit();\n\n  void clearCache(bool flushNow = true);\n\n  void clearCache(Range<rword> range);\n\n  void clearCache(RangeSet<rword> rangeSet);\n\n  uint32_t getNbExecBlock() const { return codeBlockMap.size(); }\n\n  void reduceCacheTo(uint32_t nb);\n\n  const ExecBlock *getExecBlockFromJitAddress(rword address) const {\n    auto it = codeBlockMap.find(address);\n    if (it == codeBlockMap.end()) {\n      return nullptr;\n    } else {\n      return it->second;\n    }\n  }\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/ExecBlock/X86_64/CMakeLists.txt",
    "content": "# Add QBDI target\nset(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ExecBlock_X86_64.cpp\")\n\nif(QBDI_PLATFORM_MACOS)\n  if(QBDI_ARCH_X86_64)\n    set(ASM_STUB_EXECBLOCK \"${CMAKE_CURRENT_LIST_DIR}/macos_X86_64.s\")\n  else() # QBDI_ARCH_X86\n    set(ASM_STUB_EXECBLOCK \"${CMAKE_CURRENT_LIST_DIR}/macos_X86.s\")\n  endif()\n  list(APPEND SOURCES \"${ASM_STUB_EXECBLOCK}\")\n\nelseif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n\n  if(QBDI_ARCH_X86_64)\n    set(ASM_STUB_EXECBLOCK \"${CMAKE_CURRENT_LIST_DIR}/linux_X86_64.s\")\n  else() # QBDI_ARCH_X86\n    set(ASM_STUB_EXECBLOCK \"${CMAKE_CURRENT_LIST_DIR}/linux_X86.s\")\n  endif()\n  list(APPEND SOURCES \"${ASM_STUB_EXECBLOCK}\")\n  set_property(SOURCE \"${ASM_STUB_EXECBLOCK}\" PROPERTY LINK_FLAGS\n                                                       \"-Wl,-z,noexecstack\")\n\nelseif(QBDI_PLATFORM_WINDOWS)\n\n  if(QBDI_ARCH_X86_64)\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/windows_X86_64.asm\")\n  else() # QBDI_ARCH_X86\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/windows_X86.asm\")\n  endif()\n\nelse()\n  message(FATAL_ERROR \"No stub for ${QBDI_PLATFORM} (${QBDI_ARCH})\")\nendif()\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/ExecBlock/X86_64/Context_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef CONTEXT_X86_64_H\n#define CONTEXT_X86_64_H\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n/*! X86_64 Host context.\n */\nstruct QBDI_ALIGNED(8) HostState {\n  rword sp;\n  rword fs;\n  rword gs;\n  rword selector;\n  rword callback;\n  rword data;\n  rword origin;\n  rword executeFlags;\n};\n\n/*! X86_64 Execution context.\n */\nstruct QBDI_ALIGNED(16) Context {\n\npublic:\n  // fprState needs to be first for memory alignement reasons\n  FPRState fprState;\n  GPRState gprState;\n  HostState hostState;\n};\n\n} // namespace QBDI\n\n#endif // CONTEXT_X86_64_H\n"
  },
  {
    "path": "src/ExecBlock/X86_64/ExecBlock_X86_64.cpp",
    "content": "\n/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <errno.h>\n#include <memory>\n#include <stdint.h>\n#include <vector>\n\n#include \"llvm/Support/Memory.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n#include \"QBDI/VM.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"ExecBlock/X86_64/Context_X86_64.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#if defined(QBDI_PLATFORM_WINDOWS)\nextern \"C\" void qbdi_runCodeBlock(void *codeBlock, QBDI::rword execflags);\n#else\nextern void qbdi_runCodeBlock(void *codeBlock,\n                              QBDI::rword execflags) asm(\"__qbdi_runCodeBlock\");\n#endif\n\nstatic const uint32_t MINIMAL_BLOCK_SIZE = 64;\n\nnamespace QBDI {\n\nvoid ExecBlock::selectSeq(uint16_t seqID) {\n  QBDI_REQUIRE(seqID < seqRegistry.size());\n  currentSeq = seqID;\n  currentInst = seqRegistry[currentSeq].startInstID;\n  context->hostState.selector =\n      reinterpret_cast<rword>(codeBlock.base()) +\n      static_cast<rword>(instRegistry[currentInst].offset);\n  context->hostState.executeFlags = seqRegistry[currentSeq].executeFlags;\n}\n\nvoid ExecBlock::run() {\n  if constexpr (is_ios) {\n    if (isRWRXSupported()) {\n      if (not isRX()) {\n        makeRX();\n      }\n    } else {\n      // Pages are RWX on iOS\n      llvm::sys::Memory::InvalidateInstructionCache(codeBlock.base(),\n                                                    codeBlock.allocatedSize());\n    }\n  } else {\n    if (not isRX()) {\n      makeRX();\n    }\n  }\n  if (not llvmCPUs.hasOptions(Options::OPT_DISABLE_ERRNO_BACKUP)) {\n    errno = vminstance->getErrno();\n    qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n    vminstance->setErrno(errno);\n  } else {\n    qbdi_runCodeBlock(codeBlock.base(), context->hostState.executeFlags);\n  }\n}\n\nbool ExecBlock::writePatch(std::vector<Patch>::const_iterator seqCurrent,\n                           std::vector<Patch>::const_iterator seqEnd,\n                           const LLVMCPU &llvmcpu) {\n  const Patch &p = *seqCurrent;\n\n  QBDI_REQUIRE(p.finalize);\n\n  if (getEpilogueOffset() <= MINIMAL_BLOCK_SIZE) {\n    isFull = true;\n    return false;\n  }\n\n  if (not applyRelocatedInst(p.insts, &tagRegistry, llvmcpu,\n                             MINIMAL_BLOCK_SIZE)) {\n    QBDI_DEBUG(\"Not enough space left: rollback\");\n    return false;\n  }\n  return true;\n}\n\nvoid ExecBlock::initScratchRegisterForPatch(\n    std::vector<Patch>::const_iterator seqStart,\n    std::vector<Patch>::const_iterator seqEnd) {}\n\nvoid ExecBlock::finalizeScratchRegisterForPatch() {}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBlock/X86_64/ScratchRegisterInfo_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef SCRATCHREGISTERINFO_X86_64_H\n#define SCRATCHREGISTERINFO_X86_64_H\n\nnamespace QBDI {\n\n// no need of ScratchRegister for X86 and X86_64\nstruct ScratchRegisterInfo {};\n\nstruct ScratchRegisterSeqInfo {};\n\n} // namespace QBDI\n\n#endif // SCRATCHREGISTERINFO_X86_64_H\n"
  },
  {
    "path": "src/ExecBlock/X86_64/linux_X86.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.hidden __qbdi_runCodeBlock\n.globl  __qbdi_runCodeBlock\n\n__qbdi_runCodeBlock:\n    mov eax, [esp+4]\n    mov ecx, [esp+8]\n    test ecx, 2;\n    jz _skip_save_fpu;\n    sub esp, 8;\n    stmxcsr [esp];\n    fnstcw [esp+4];\n_skip_save_fpu:\n    pushad;\n    call eax;\n    popad;\n    test ecx, 2;\n    jz _skip_restore_fpu;\n    fninit;\n    fldcw [esp+4];\n    ldmxcsr [esp];\n    add esp, 8;\n_skip_restore_fpu:\n    cld;\n    ret;\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",@progbits\n"
  },
  {
    "path": "src/ExecBlock/X86_64/linux_X86_64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.hidden __qbdi_runCodeBlock\n.globl  __qbdi_runCodeBlock\n\n__qbdi_runCodeBlock:\n    test rsi, 2;\n    jz _skip_save_fpu;\n    sub rsp, 8;\n    stmxcsr [rsp];\n    fnstcw [rsp+4];\n_skip_save_fpu:\n    push r15;\n    push r14;\n    push r13;\n    push r12;\n    push rbp;\n    push rsi;\n    push rbx;\n    call rdi;\n    pop rbx;\n    pop rsi;\n    pop rbp;\n    pop r12;\n    pop r13;\n    pop r14;\n    pop r15;\n    test rsi, 2;\n    jz _skip_restore_fpu;\n    fninit;\n    fldcw [rsp+4];\n    ldmxcsr [rsp];\n    add rsp, 8;\n_skip_restore_fpu:\n    cld;\n    ret;\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",@progbits\n"
  },
  {
    "path": "src/ExecBlock/X86_64/macos_X86.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.private_extern __qbdi_runCodeBlock\n\n__qbdi_runCodeBlock:\n    mov eax, [esp+4]\n    mov ecx, [esp+8]\n    test ecx, 2;\n    jz _skip_save_fpu;\n    sub esp, 8;\n    stmxcsr [esp];\n    fnstcw [esp+4];\n_skip_save_fpu:\n    pushad;\n    call eax;\n    popad;\n    test ecx, 2;\n    jz _skip_restore_fpu;\n    fninit;\n    fldcw [esp+4];\n    ldmxcsr [esp];\n    add esp, 8;\n_skip_restore_fpu:\n    cld;\n    ret;\n"
  },
  {
    "path": "src/ExecBlock/X86_64/macos_X86_64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.private_extern __qbdi_runCodeBlock\n\n__qbdi_runCodeBlock:\n    test rsi, 2;\n    jz _skip_save_fpu;\n    sub rsp, 8;\n    stmxcsr [rsp];\n    fnstcw [rsp+4];\n_skip_save_fpu:\n    push r15;\n    push r14;\n    push r13;\n    push r12;\n    push rbp;\n    push rsi;\n    push rbx;\n    call rdi;\n    pop rbx;\n    pop rsi;\n    pop rbp;\n    pop r12;\n    pop r13;\n    pop r14;\n    pop r15;\n    test rsi, 2;\n    jz _skip_restore_fpu;\n    fninit;\n    fldcw [rsp+4];\n    ldmxcsr [rsp];\n    add rsp, 8;\n_skip_restore_fpu:\n    cld;\n    ret;\n"
  },
  {
    "path": "src/ExecBlock/X86_64/windows_X86.asm",
    "content": "; This file is part of QBDI.\n;\n; Copyright 2017 - 2025 Quarkslab\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\n.386\n.xmm\n.model flat, C\n\n_TEXT segment\n\nPUBLIC qbdi_runCodeBlock\n\n.CODE\n\nqbdi_runCodeBlock PROC\n    mov eax, [esp+4];\n    mov ecx, [esp+8];\n    test ecx, 2;\n    jz _skip_save_fpu;\n    sub esp, 8;\n    stmxcsr [esp];\n    fnstcw [esp+4];\n_skip_save_fpu:\n    pushad;\n    call eax;\n    popad;\n    test ecx, 2;\n    jz _skip_restore_fpu;\n    fninit;\n    fldcw [esp+4];\n    ldmxcsr [esp];\n    add esp, 8;\n_skip_restore_fpu:\n    cld;\n    ret;\nqbdi_runCodeBlock ENDP\n\nEND\n"
  },
  {
    "path": "src/ExecBlock/X86_64/windows_X86_64.asm",
    "content": "; This file is part of QBDI.\n;\n; Copyright 2017 - 2025 Quarkslab\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\nPUBLIC qbdi_runCodeBlock\n\n.CODE\n\nqbdi_runCodeBlock PROC\n    test rdx, 2;\n    jz _skip_save_fpu;\n    mov r10, rsp;\n    sub rsp, 168;\n    and rsp, -16;\n    movaps [rsp], xmm6;\n    movaps [rsp+16], xmm7;\n    movaps [rsp+32], xmm8;\n    movaps [rsp+48], xmm9;\n    movaps [rsp+64], xmm10;\n    movaps [rsp+80], xmm11;\n    movaps [rsp+96], xmm12;\n    movaps [rsp+112], xmm13;\n    movaps [rsp+128], xmm14;\n    movaps [rsp+144], xmm15;\n    stmxcsr [rsp+160];\n    fnstcw [rsp+164];\n    push r10;\n_skip_save_fpu:\n    push r15;\n    push r14;\n    push r13;\n    push r12;\n    push rbp;\n    push rdi;\n    push rsi;\n    push rdx;\n    push rbx;\n    call rcx;\n    pop rbx;\n    pop rdx;\n    pop rsi;\n    pop rdi;\n    pop rbp;\n    pop r12;\n    pop r13;\n    pop r14;\n    pop r15;\n    test rdx, 2;\n    jz _skip_restore_fpu;\n    pop r10;\n    fninit;\n    fldcw [rsp+164];\n    ldmxcsr [rsp+160];\n    movaps xmm15, [rsp+144];\n    movaps xmm14, [rsp+128];\n    movaps xmm13, [rsp+112];\n    movaps xmm12, [rsp+96];\n    movaps xmm11, [rsp+80];\n    movaps xmm10, [rsp+64];\n    movaps xmm9, [rsp+48];\n    movaps xmm8, [rsp+32];\n    movaps xmm7, [rsp+16];\n    movaps xmm6, [rsp];\n    mov rsp, r10;\n_skip_restore_fpu:\n    cld;\n    ret;\nqbdi_runCodeBlock ENDP\n\nEND\n"
  },
  {
    "path": "src/ExecBroker/AARCH64/ExecBroker_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"QBDI/PtrAuth.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"ExecBroker/ExecBroker.h\"\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/PatchGenerator_AARCH64.h\"\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Patch/Patch.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"llvm/MC/MCInst.h\"\n\nnamespace QBDI {\nstatic const size_t SCAN_DISTANCE = 2;\n\nvoid ExecBroker::initExecBrokerSequences(const LLVMCPUs &llvmCPUs) {\n  llvm::MCInst nopInst = nop();\n  const LLVMCPU &llvmCPU = llvmCPUs.getCPU(CPUMode::DEFAULT);\n\n  // Sequence Broker with LR\n  // =======================\n  {\n    QBDI_DEBUG(\"Create Sequence Broker LR\");\n    QBDI::Patch::Vec brokerLR;\n    brokerLR.push_back(QBDI::Patch(nopInst, 0x4, 4, llvmCPU));\n    Patch &pLR = brokerLR[0];\n    pLR.finalize = true;\n\n    // target BTIj\n    pLR.append(RelocTag::unique(RelocTagPatchBegin));\n    pLR.append(GenBTI().genReloc(llvmCPU));\n\n    // Prepare to Jump\n    // 1. Store jumpAddress in LR\n    pLR.append(\n        Ldr(Reg(REG_LR), Offset(offsetof(Context, hostState.brokerAddr))));\n\n    // 2. backup TPIDR and restore X28 and SR\n    pLR.append(FullRegisterRestore(true).genReloc(llvmCPU));\n\n    // 3. Jump to the target. Use BLR to change LR to the hook return address\n    pLR.append(Blr(Reg(REG_LR)));\n\n    // use RelocTagPatchInstEnd to mark the return address\n    pLR.append(RelocTag::unique(RelocTagPatchInstBegin));\n    pLR.append(RelocTag::unique(RelocTagPatchInstEnd));\n\n    // Hook\n    // Backup X28 and SR and reset SR to base addr\n    pLR.append(FullRegisterReset(true).genReloc(llvmCPU));\n\n    // This sequence doesn't need a terminator\n    pLR.setModifyPC(true);\n\n    // Write the Patch in the ExecBlock\n    SeqWriteResult res =\n        transferBlock->writeSequence(brokerLR.begin(), brokerLR.end());\n\n    QBDI_REQUIRE_ABORT(res.patchWritten == 1, \"Fail to write Sequence Broker\");\n\n    uint16_t instID = transferBlock->getSeqStart(res.seqID);\n\n    std::vector<TagInfo> retAddr =\n        transferBlock->queryTagByInst(instID, RelocTagPatchInstEnd);\n    QBDI_REQUIRE(retAddr.size() == 1);\n\n    archData.transfertLR.seqID = res.seqID;\n    archData.transfertLR.hook = transferBlock->getAddressTag(retAddr[0]);\n    QBDI_DEBUG(\"Sequence Broker LR: id={} hook=0x{:x}\",\n               archData.transfertLR.seqID, archData.transfertLR.hook);\n  }\n\n  // Sequence Broker with X28\n  // ========================\n  {\n    QBDI_DEBUG(\"Create Sequence Broker X28\");\n    QBDI::Patch::Vec brokerX28;\n    brokerX28.push_back(QBDI::Patch(nopInst, 0x4, 4, llvmCPU));\n    Patch &pX28 = brokerX28[0];\n    pX28.finalize = true;\n\n    // target BTIj\n    pX28.append(RelocTag::unique(RelocTagPatchBegin));\n    pX28.append(GenBTI().genReloc(llvmCPU));\n\n    // Prepare to Jump\n    // 1. Store jumpAddress in X28\n    pX28.append(Ldr(Reg(28), Offset(offsetof(Context, hostState.brokerAddr))));\n\n    // 2. Restore ScratchRegister\n    pX28.append(FullRegisterRestore(false).genReloc(llvmCPU));\n\n    // 3. Jump to the target using X28.\n    // Don't link as LR does'nt have a return address\n    pX28.append(Br(Reg(28)));\n\n    // use RelocTagPatchInstEnd to mark the return address\n    pX28.append(RelocTag::unique(RelocTagPatchInstBegin));\n    pX28.append(RelocTag::unique(RelocTagPatchInstEnd));\n\n    // Hook\n    // 1. Backup SR and reset it to base address\n    pX28.append(FullRegisterReset(false).genReloc(llvmCPU));\n\n    // This sequence doesn't need a terminator\n    pX28.setModifyPC(true);\n\n    // Write the Patch in the ExecBlock\n    SeqWriteResult res =\n        transferBlock->writeSequence(brokerX28.begin(), brokerX28.end());\n    QBDI_REQUIRE_ABORT(res.patchWritten == 1, \"Fail to write Sequence Broker\");\n\n    uint16_t instID = transferBlock->getSeqStart(res.seqID);\n\n    std::vector<TagInfo> retAddr =\n        transferBlock->queryTagByInst(instID, RelocTagPatchInstEnd);\n    QBDI_REQUIRE(retAddr.size() == 1);\n\n    archData.transfertX28.seqID = res.seqID;\n    archData.transfertX28.hook = transferBlock->getAddressTag(retAddr[0]);\n\n    QBDI_DEBUG(\"Sequence Broker X28: id={} hook=0x{:x}\",\n               archData.transfertX28.seqID, archData.transfertX28.hook);\n  }\n}\n\nrword *ExecBroker::getReturnPoint(GPRState *gprState) const {\n  auto ptr = reinterpret_cast<rword *>(gprState->sp);\n\n  if (isInstrumented(strip_ptrauth(gprState->lr))) {\n    QBDI_DEBUG(\"Found instrumented return address in LR register\");\n    return &(gprState->lr);\n  }\n\n  for (size_t i = 0; i < SCAN_DISTANCE; ++i) {\n    if (isInstrumented(strip_ptrauth(ptr[i]))) {\n      QBDI_DEBUG(\"Found instrumented return address on the stack at {:p}\",\n                 reinterpret_cast<void *>(&(ptr[i])));\n      return &(ptr[i]);\n    }\n  }\n\n  QBDI_DEBUG(\"LR register does not contain an instrumented return address\");\n  return nullptr;\n}\n\nbool ExecBroker::transferExecution(rword addr, GPRState *gprState,\n                                   FPRState *fprState) {\n\n  // Search all return address\n  rword *ptr = getReturnPoint(gprState);\n  rword *ptr2 = nullptr;\n  rword returnAddress = *ptr;\n\n  if constexpr (is_linux) {\n\n    /* On Linux, the resolution of a plt symbol may use the follow code:\n     *\n     * 0000000000000820 <.plt>:\n     *  820:   a9bf7bf0        stp     x16, x30, [sp, #-16]!\n     *  824:   90000090        adrp    x16, 10000 <__FRAME_END__+0xf430>\n     *  828:   f947fe11        ldr     x17, [x16, #4088]\n     *  82c:   913fe210        add     x16, x16, #0xff8\n     *  830:   d61f0220        br      x17\n     *  834:   d503201f        nop\n     *  838:   d503201f        nop\n     *  83c:   d503201f        nop\n     *\n     * 0000000000000840 <__cxa_finalize@plt>:\n     *  840:   b0000090        adrp    x16, 11000 <__cxa_finalize@GLIBC_2.17>\n     *  844:   f9400211        ldr     x17, [x16]\n     *  848:   91000210        add     x16, x16, #0x0\n     *  84c:   d61f0220        br      x17\n     *\n     * If the symbol isn't resolved, the return address is in lr (x30) register\n     * and on the stack. We need to change both of them.\n     */\n    if (ptr == &(gprState->lr)) {\n      rword *sp = reinterpret_cast<rword *>(gprState->sp);\n\n      for (size_t i = 0; i < SCAN_DISTANCE; ++i) {\n        if (sp[i] == returnAddress) {\n          QBDI_DEBUG(\"TransferExecution: Found return address also at {:}\",\n                     reinterpret_cast<void *>(&(sp[i])));\n\n          ptr2 = &(sp[i]);\n          break;\n        }\n      }\n    }\n  }\n  rword hook;\n\n  if (ptr == &(gprState->lr)) {\n    hook = archData.transfertLR.hook;\n    QBDI_DEBUG(\n        \"TransferExecution: Used LR as a jumpRegister. \"\n        \"Return address 0x{:} replace with 0x{:x}\",\n        returnAddress, hook);\n    transferBlock->selectSeq(archData.transfertLR.seqID);\n  } else {\n    hook = archData.transfertX28.hook;\n    QBDI_DEBUG(\n        \"TransferExecution: Used x28 as a jumpRegister. \"\n        \"Return address 0x{:} replace with 0x{:x}\",\n        returnAddress, hook);\n    transferBlock->selectSeq(archData.transfertX28.seqID);\n  }\n\n  // set fake return address\n  *ptr = hook;\n  if (ptr2 != nullptr) {\n    *ptr2 = hook;\n  }\n\n  // Write transfer state\n  transferBlock->getContext()->gprState = *gprState;\n  transferBlock->getContext()->fprState = *fprState;\n\n  // Set the jump address in hostate.brokerAddr\n  transferBlock->getContext()->hostState.brokerAddr = addr;\n\n  // Execute transfer\n  QBDI_DEBUG(\"Transfering execution to 0x{:x} using transferBlock 0x{:x}\", addr,\n             reinterpret_cast<uintptr_t>(&*transferBlock));\n\n  transferBlock->run();\n\n  // Read transfer result\n  *gprState = transferBlock->getContext()->gprState;\n  *fprState = transferBlock->getContext()->fprState;\n\n  // Restore original return\n  QBDI_GPR_SET(gprState, REG_PC, returnAddress);\n  if (QBDI_GPR_GET(gprState, REG_LR) == hook) {\n    QBDI_GPR_SET(gprState, REG_LR, returnAddress);\n  }\n  if constexpr (is_linux) {\n    if (ptr2 != nullptr and *ptr2 == hook) {\n      *ptr2 = returnAddress;\n    }\n  }\n\n  return true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBroker/AARCH64/ExecBroker_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_EXECBROKER_ARM64_H\n#define QBDI_EXECBROKER_ARM64_H\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nstruct ExecBrokerSequenceTransfert {\n  uint16_t seqID;\n  rword hook;\n};\n\nstruct ExecBrokerArchData {\n  ExecBrokerSequenceTransfert transfertLR;\n  ExecBrokerSequenceTransfert transfertX28;\n};\n\n} // namespace QBDI\n\n#endif // QBDI_EXECBROKER_ARM64_H\n"
  },
  {
    "path": "src/ExecBroker/ARM/ExecBroker_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"QBDI/State.h\"\n#include \"ExecBlock/Context.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"ExecBroker/ExecBroker.h\"\n#include \"Patch/ExecBlockFlags.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nvoid ExecBroker::initExecBrokerSequences(const LLVMCPUs &llvmCPUs) {}\n\nrword *ExecBroker::getReturnPoint(GPRState *gprState) const {\n  static int SCAN_DISTANCE = 3;\n  rword *ptr = reinterpret_cast<rword *>(gprState->sp);\n\n  if (isInstrumented(gprState->lr)) {\n    QBDI_DEBUG(\"Found instrumented return address in LR register\");\n    return &(gprState->lr);\n  }\n\n  for (int i = 0; i < SCAN_DISTANCE; i++) {\n    if (isInstrumented(ptr[i])) {\n      QBDI_DEBUG(\"Found instrumented return address on the stack at {:p}\",\n                 reinterpret_cast<void *>(&(ptr[i])));\n      return &(ptr[i]);\n    }\n  }\n  QBDI_DEBUG(\"No instrumented return address found on the stack (lr : 0x{:x})\",\n             gprState->lr);\n  return nullptr;\n}\n\nbool ExecBroker::transferExecution(rword addr, GPRState *gprState,\n                                   FPRState *fprState) {\n  rword hook = 0;\n\n  // Backup / Patch return address\n  hook = transferBlock->getCurrentPC() + transferBlock->getEpilogueOffset();\n  rword *ptr = getReturnPoint(gprState);\n  rword hookedAddress = *ptr;\n  *ptr = hook;\n  QBDI_DEBUG(\n      \"TransferExecution: Patched {:} hooking return address 0x{:06x} with \"\n      \"0x{:06x}\",\n      reinterpret_cast<void *>(ptr), hookedAddress, hook);\n\n  // Write transfer state\n  transferBlock->getContext()->gprState = *gprState;\n  transferBlock->getContext()->fprState = *fprState;\n  transferBlock->getContext()->hostState.selector = addr;\n  transferBlock->getContext()->hostState.executeFlags = defaultExecuteFlags;\n  // Execute transfer\n  QBDI_DEBUG(\"Transfering execution to 0x{:x} using transferBlock 0x{:x}\", addr,\n             reinterpret_cast<uintptr_t>(&*transferBlock));\n\n  transferBlock->run();\n\n  // Read transfer result\n  *gprState = transferBlock->getContext()->gprState;\n  *fprState = transferBlock->getContext()->fprState;\n\n  // Restore original return\n  QBDI_GPR_SET(gprState, REG_PC, hookedAddress);\n\n  return true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBroker/ARM/ExecBroker_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_EXECBROKER_ARM_H\n#define QBDI_EXECBROKER_ARM_H\n\nnamespace QBDI {\n\nstruct ExecBrokerArchData {};\n\n} // namespace QBDI\n\n#endif // QBDI_EXECBROKER_ARM_H\n"
  },
  {
    "path": "src/ExecBroker/CMakeLists.txt",
    "content": "# Add QBDI target\nset(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ExecBroker.cpp\")\n\nif(QBDI_ARCH_X86 OR QBDI_ARCH_X86_64)\n  target_sources(\n    QBDI_src INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/X86_64/ExecBroker_X86_64.cpp\")\nelseif(QBDI_ARCH_ARM)\n  target_sources(QBDI_src\n                 INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/ARM/ExecBroker_ARM.cpp\")\nelseif(QBDI_ARCH_AARCH64)\n  target_sources(\n    QBDI_src\n    INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/AARCH64/ExecBroker_AARCH64.cpp\")\nelse()\n  message(FATAL_ERROR \"Missing ExecBroker for ${QBDI_ARCH}\")\nendif()\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/ExecBroker/ExecBroker.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <utility>\n\n#include \"llvm/Support/Error.h\"\n#include \"llvm/Support/Process.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"ExecBroker/ExecBroker.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nExecBroker::ExecBroker(std::unique_ptr<ExecBlock> _transferBlock,\n                       const LLVMCPUs &llvmCPUs, VMInstanceRef vminstance)\n    : transferBlock(std::move(_transferBlock)) {\n  pageSize = llvm::expectedToOptional(llvm::sys::Process::getPageSize())\n                 .value_or(4096);\n  initExecBrokerSequences(llvmCPUs);\n}\n\nvoid ExecBroker::changeVMInstanceRef(VMInstanceRef vminstance) {\n  transferBlock->changeVMInstanceRef(vminstance);\n}\n\nvoid ExecBroker::addInstrumentedRange(const Range<rword> &r) {\n  QBDI_DEBUG(\"Adding instrumented range [0x{:x}, 0x{:x}]\", r.start(), r.end());\n  instrumented.add(r);\n}\n\nvoid ExecBroker::removeInstrumentedRange(const Range<rword> &r) {\n  QBDI_DEBUG(\"Removing instrumented range [0x{:x}, 0x{:x}]\", r.start(),\n             r.end());\n  instrumented.remove(r);\n}\n\nvoid ExecBroker::removeAllInstrumentedRanges() { instrumented.clear(); }\n\nbool ExecBroker::addInstrumentedModule(const std::string &name) {\n  bool instrumented = false;\n  if (name.empty()) {\n    return false;\n  }\n\n  for (const MemoryMap &m : getCurrentProcessMaps()) {\n    if ((m.name == name) && (m.permission & QBDI::PF_EXEC)) {\n      addInstrumentedRange(m.range);\n      instrumented = true;\n    }\n  }\n  return instrumented;\n}\n\nbool ExecBroker::addInstrumentedModuleFromAddr(rword addr) {\n  for (const MemoryMap &m : getCurrentProcessMaps()) {\n    if (m.range.contains(addr)) {\n      if (not m.name.empty()) {\n        return addInstrumentedModule(m.name);\n      } else if (m.permission & QBDI::PF_EXEC) {\n        addInstrumentedRange(m.range);\n        return true;\n      } else {\n        return false;\n      }\n    }\n  }\n  return false;\n}\n\nbool ExecBroker::removeInstrumentedModule(const std::string &name) {\n  bool removed = false;\n\n  for (const MemoryMap &m : getCurrentProcessMaps()) {\n    if (m.name == name) {\n      removeInstrumentedRange(m.range);\n      removed = true;\n    }\n  }\n  return removed;\n}\n\nbool ExecBroker::removeInstrumentedModuleFromAddr(rword addr) {\n  for (const MemoryMap &m : getCurrentProcessMaps()) {\n    if (m.range.contains(addr)) {\n      removeInstrumentedRange(m.range);\n      if (not m.name.empty()) {\n        removeInstrumentedModule(m.name);\n      }\n      return true;\n    }\n  }\n  return false;\n}\n\nbool ExecBroker::instrumentAllExecutableMaps() {\n  bool instrumented = false;\n\n  for (const MemoryMap &m : getCurrentProcessMaps()) {\n    if (m.permission & QBDI::PF_EXEC) {\n      addInstrumentedRange(m.range);\n      instrumented = true;\n    }\n  }\n  return instrumented;\n}\n\nbool ExecBroker::canTransferExecution(GPRState *gprState) const {\n  return getReturnPoint(gprState) ? true : false;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBroker/ExecBroker.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_EXECBROKER_H\n#define QBDI_EXECBROKER_H\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"llvm/Support/Memory.h\"\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n#include \"ExecBlock/ExecBlock.h\"\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n#include \"ExecBroker/X86_64/ExecBroker_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"ExecBroker/ARM/ExecBroker_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"ExecBroker/AARCH64/ExecBroker_AARCH64.h\"\n#else\n#error \"No Implementation of ExecBroker for the current ARCH\"\n#endif\n\nnamespace QBDI {\nclass LLVMCPUs;\n\nclass ExecBroker {\n\nprivate:\n  RangeSet<rword> instrumented;\n  std::unique_ptr<ExecBlock> transferBlock;\n  rword pageSize;\n\n  using PF = llvm::sys::Memory::ProtectionFlags;\n\n  // ARCH specific method\n  ExecBrokerArchData archData;\n\n  void initExecBrokerSequences(const LLVMCPUs &llvmCPUs);\n  rword *getReturnPoint(GPRState *gprState) const;\n\npublic:\n  ExecBroker(std::unique_ptr<ExecBlock> transferBlock, const LLVMCPUs &llvmCPUs,\n             VMInstanceRef vminstance = nullptr);\n\n  void changeVMInstanceRef(VMInstanceRef vminstance);\n\n  bool isInstrumented(rword addr) const { return instrumented.contains(addr); }\n\n  void setInstrumentedRange(const RangeSet<rword> &r) { instrumented = r; }\n\n  const RangeSet<rword> &getInstrumentedRange() const { return instrumented; }\n\n  void addInstrumentedRange(const Range<rword> &r);\n  bool addInstrumentedModule(const std::string &name);\n  bool addInstrumentedModuleFromAddr(rword addr);\n\n  void removeInstrumentedRange(const Range<rword> &r);\n  bool removeInstrumentedModule(const std::string &name);\n  bool removeInstrumentedModuleFromAddr(rword addr);\n  void removeAllInstrumentedRanges();\n\n  bool instrumentAllExecutableMaps();\n\n  bool canTransferExecution(GPRState *gprState) const;\n\n  bool transferExecution(rword addr, GPRState *gprState, FPRState *fprState);\n};\n\n} // namespace QBDI\n\n#endif // QBDI_EXECBROKER_H\n"
  },
  {
    "path": "src/ExecBroker/X86_64/ExecBroker_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <memory>\n#include <stdint.h>\n\n#include \"QBDI/State.h\"\n#include \"ExecBlock/Context.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"ExecBroker/ExecBroker.h\"\n#include \"Patch/ExecBlockFlags.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\nclass LLVMCPUs;\n\nvoid ExecBroker::initExecBrokerSequences(const LLVMCPUs &llvmCPUs) {}\n\nrword *ExecBroker::getReturnPoint(GPRState *gprState) const {\n  static int SCAN_DISTANCE = 3;\n  rword *ptr = (rword *)QBDI_GPR_GET(gprState, REG_SP);\n\n  for (int i = 0; i < SCAN_DISTANCE; i++) {\n    if (isInstrumented(ptr[i])) {\n      QBDI_DEBUG(\"Found instrumented return address on the stack at {:p}\",\n                 reinterpret_cast<void *>(&(ptr[i])));\n      return &(ptr[i]);\n    }\n  }\n  QBDI_DEBUG(\"No instrumented return address found on the stack\");\n  return nullptr;\n}\n\nbool ExecBroker::transferExecution(rword addr, GPRState *gprState,\n                                   FPRState *fprState) {\n  rword hook = 0;\n\n  // Backup / Patch return address\n  hook = transferBlock->getCurrentPC() + transferBlock->getEpilogueOffset();\n  rword *ptr = getReturnPoint(gprState);\n  rword hookedAddress = *ptr;\n  *ptr = hook;\n  QBDI_DEBUG(\n      \"TransferExecution: Patched {:} hooking return address 0x{:06x} with \"\n      \"0x{:06x}\",\n      reinterpret_cast<void *>(ptr), hookedAddress, hook);\n\n  // Write transfer state\n  transferBlock->getContext()->gprState = *gprState;\n  transferBlock->getContext()->fprState = *fprState;\n  transferBlock->getContext()->hostState.selector = addr;\n  transferBlock->getContext()->hostState.executeFlags = defaultExecuteFlags;\n  // Execute transfer\n  QBDI_DEBUG(\"Transfering execution to 0x{:x} using transferBlock 0x{:x}\", addr,\n             reinterpret_cast<uintptr_t>(&*transferBlock));\n\n  transferBlock->run();\n\n  // Read transfer result\n  *gprState = transferBlock->getContext()->gprState;\n  *fprState = transferBlock->getContext()->fprState;\n\n  // Restore original return\n  QBDI_GPR_SET(gprState, REG_PC, hookedAddress);\n\n  return true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/ExecBroker/X86_64/ExecBroker_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_EXECBROKER_X86_64_H\n#define QBDI_EXECBROKER_X86_64_H\n\nnamespace QBDI {\n\nstruct ExecBrokerArchData {};\n\n} // namespace QBDI\n\n#endif // QBDI_EXECBROKER_X86_64_H\n"
  },
  {
    "path": "src/Patch/AARCH64/CMakeLists.txt",
    "content": "set(SOURCES\n    \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockFlags_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockPatch_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstInfo_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstrRules_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Layer2_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccess_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchCondition_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchGenerator_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchRuleAssembly_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Register_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/RelocatableInst_AARCH64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/TempManager_AARCH64.cpp\")\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Patch/AARCH64/ExecBlockFlags_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Patch/ExecBlockFlags.h\"\n\nnamespace QBDI {\n\nconst uint8_t defaultExecuteFlags = ExecBlockFlags::Default;\n\nuint8_t getExecBlockFlags(const llvm::MCInst &inst,\n                          const QBDI::LLVMCPU &llvmcpu) {\n  return defaultExecuteFlags;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/ExecBlockFlags_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef ExecBlockFlags_AARCH64_H\n#define ExecBlockFlags_AARCH64_H\n\n#include <stdint.h>\n\nnamespace QBDI {\n\ntypedef enum : uint8_t { Default = 0 } ExecBlockFlags;\n\n}\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/ExecBlockPatch_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"AArch64InstrInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/AARCH64/ExecBlockPatch_AARCH64.h\"\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/PatchGenerator_AARCH64.h\"\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Patch/ExecBlockPatch.h\"\n#include \"Patch/RelocatableInst.h\"\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"QBDI/Options.h\"\n\nnamespace QBDI {\n\nRelocatableInst::UniquePtrVec getExecBlockPrologue(const LLVMCPU &llvmcpu) {\n  Options opts = llvmcpu.getOptions();\n  RelocatableInst::UniquePtrVec prologue;\n\n  prologue.push_back(BTIc());\n\n  // X28 is used to address the DataBlock\n  prologue.push_back(SetBaseAddress::unique(Reg(28)));\n\n  // Save return address\n  // ===================\n  prologue.push_back(StrPre(Reg(REG_LR), Reg(REG_SP), Constant(-16)));\n\n  // Save Host SP\n  // ============\n  prologue.push_back(Mov(Reg(0), Reg(REG_SP)));\n  prologue.push_back(\n      Str(Reg(0), Reg(28), Offset(offsetof(Context, hostState.sp))));\n\n  if ((opts & Options::OPT_DISABLE_FPR) == 0) {\n    // Restore SIMD\n    // ============\n    prologue.push_back(\n        Add(Reg(0), Reg(28), Constant(offsetof(Context, fprState.v0))));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q0_Q1_Q2_Q3, Reg(0)));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q4_Q5_Q6_Q7, Reg(0)));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q8_Q9_Q10_Q11, Reg(0)));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q12_Q13_Q14_Q15, Reg(0)));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q16_Q17_Q18_Q19, Reg(0)));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q20_Q21_Q22_Q23, Reg(0)));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q24_Q25_Q26_Q27, Reg(0)));\n    prologue.push_back(Ld1PostInc(llvm::AArch64::Q28_Q29_Q30_Q31, Reg(0)));\n\n    // Restore FPCR and FPSR\n    // =====================\n    static_assert(offsetof(FPRState, fpcr) + 8 == offsetof(FPRState, fpsr));\n    static_assert(offsetof(Context, fprState.fpcr) ==\n                  offsetof(Context, fprState.v31) + sizeof(__uint128_t));\n    prologue.push_back(LdpPost(Reg(1), Reg(2), Reg(0), Constant(16)));\n    prologue.push_back(WriteFPCR(Reg(1)));\n    prologue.push_back(WriteFPSR(Reg(2)));\n  }\n  prologue.push_back(\n      Add(Reg(0), Reg(28), Constant(offsetof(Context, gprState))));\n\n  // Restore Stack and NZCV\n  // ======================\n  static_assert(offsetof(GPRState, sp) + 8 == offsetof(GPRState, nzcv));\n  prologue.push_back(Ldp(Reg(1), Reg(2), Reg(0), offsetof(GPRState, sp)));\n  prologue.push_back(WriteNZCV(Reg(2)));\n  prologue.push_back(Mov(Reg(REG_SP), Reg(1)));\n\n  // Restore LR and X29\n  // ==================\n  prologue.push_back(Ldp(Reg(29), Reg(30), Reg(0), offsetof(GPRState, x29)));\n\n  // Skip restore of X28. It will be restore by the instrumented block if\n  // needed.\n\n  // Load other registers\n  // ====================\n  for (int i = 26; i >= 0; i -= 2) {\n    if constexpr (is_macos or is_ios) {\n      if (i == 18) {\n        // skip x18 that is platform reserved\n        prologue.push_back(Ldr(Reg(i + 1), Reg(0), (i + 1) * sizeof(rword)));\n      } else {\n        prologue.push_back(Ldp(Reg(i), Reg(i + 1), Reg(0), i * sizeof(rword)));\n      }\n    } else {\n      prologue.push_back(Ldp(Reg(i), Reg(i + 1), Reg(0), i * sizeof(rword)));\n    }\n  }\n\n  // Jump selector\n  // =============\n  prologue.push_back(\n      Ldr(Reg(28), Reg(28), Offset(offsetof(Context, hostState.selector))));\n\n  prologue.push_back(Br(Reg(28)));\n\n  return prologue;\n}\n\nRelocatableInst::UniquePtrVec getExecBlockEpilogue(const LLVMCPU &llvmcpu) {\n  Options opts = llvmcpu.getOptions();\n  RelocatableInst::UniquePtrVec epilogue;\n\n  // X28 is used to address the DataBlock\n  // The saved value of X28 is saved by the instrumented block\n  // The epilogue is jitted at the end of the block\n  epilogue.push_back(SetBaseAddress::unique(Reg(28)));\n\n  // Save GPR from the guest\n  // =======================\n  for (unsigned i = 0; i < 28; i += 2) {\n    if constexpr (is_macos or is_ios) {\n      if (i == 18) {\n        // skip x18 that is platform reserved\n        epilogue.push_back(\n            Str(Reg(i + 1), Reg(28),\n                offsetof(Context, gprState) + (i + 1) * sizeof(rword)));\n      } else {\n        epilogue.push_back(\n            Stp(Reg(i), Reg(i + 1), Reg(28),\n                offsetof(Context, gprState) + i * sizeof(rword)));\n      }\n    } else {\n      epilogue.push_back(Stp(Reg(i), Reg(i + 1), Reg(28),\n                             offsetof(Context, gprState) + i * sizeof(rword)));\n    }\n  }\n\n  // Save X29 and LR\n  // ===============\n  epilogue.push_back(\n      Stp(Reg(29), Reg(30), Reg(28), offsetof(Context, gprState.x29)));\n\n  // Save stack and NZCV\n  // ===================\n  static_assert(offsetof(GPRState, sp) + 8 == offsetof(GPRState, nzcv));\n  epilogue.push_back(ReadNZCV(Reg(1)));\n  epilogue.push_back(Mov(Reg(0), Reg(REG_SP)));\n  epilogue.push_back(\n      Stp(Reg(0), Reg(1), Reg(28), offsetof(Context, gprState.sp)));\n\n  if ((opts & Options::OPT_DISABLE_FPR) == 0) {\n    // Save FPCR and FPSR\n    // ==================\n    static_assert(offsetof(FPRState, fpcr) + 8 == offsetof(FPRState, fpsr));\n\n    // set X0 at the beginning of the FPRState\n    epilogue.push_back(\n        Add(Reg(0), Reg(28), Constant(offsetof(Context, fprState.v0))));\n\n    // Get FPCR and FPSR\n    // =================\n    epilogue.push_back(ReadFPCR(Reg(1)));\n    epilogue.push_back(ReadFPSR(Reg(2)));\n\n    // Save FPR\n    // ========\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q0_Q1_Q2_Q3, Reg(0)));\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q4_Q5_Q6_Q7, Reg(0)));\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q8_Q9_Q10_Q11, Reg(0)));\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q12_Q13_Q14_Q15, Reg(0)));\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q16_Q17_Q18_Q19, Reg(0)));\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q20_Q21_Q22_Q23, Reg(0)));\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q24_Q25_Q26_Q27, Reg(0)));\n    epilogue.push_back(St1PostInc(llvm::AArch64::Q28_Q29_Q30_Q31, Reg(0)));\n\n    // Set FPCR and FPSR\n    // =================\n    static_assert(offsetof(Context, fprState.fpcr) ==\n                  offsetof(Context, fprState.v31) + sizeof(__uint128_t));\n    epilogue.push_back(Stp(Reg(1), Reg(2), Reg(0), 0));\n  }\n\n  // Restore Host SP\n  // ===============\n  epilogue.push_back(\n      Ldr(Reg(0), Reg(28), Offset(offsetof(Context, hostState.sp))));\n\n  epilogue.push_back(Mov(Reg(REG_SP), Reg(0)));\n\n  // Return to host\n  // ==============\n  epilogue.push_back(LdrPost(Reg(REG_LR), Reg(REG_SP), Constant(16)));\n\n  epilogue.push_back(Ret());\n  return epilogue;\n}\n\n// Patch allowing to terminate a basic block early by writing address into\n// DataBlock[Offset(PC)]\nRelocatableInst::UniquePtrVec getTerminator(const LLVMCPU &llvmcpu,\n                                            rword address) {\n  RelocatableInst::UniquePtrVec terminator;\n\n  // X28 is never restore by the prologue/epilogue.\n  // It can be used as a temp register without needed to restore it\n  // except when the instruction uses X28.\n  // The Terminator is never include in a Patch, X28 is always available\n\n  terminator.push_back(Mov(Reg(28), Constant(address)));\n  terminator.push_back(Str(Reg(28), Offset(Reg(REG_PC))));\n\n  return terminator;\n}\n\n// Change ScratchRegister\nRelocatableInst::UniquePtrVec\nchangeScratchRegister(const LLVMCPU &llvmcpu, RegLLVM oldSR, RegLLVM nextSR_) {\n\n  QBDI_REQUIRE_ABORT(getGPRPosition(nextSR_) != ((size_t)-1),\n                     \"Unexpected next ScratchRegister {}\",\n                     llvmcpu.getRegisterName(nextSR_));\n\n  Reg nextSR = getGPRPosition(nextSR_);\n\n  // use X28\n  Reg tmp = 28;\n  QBDI_REQUIRE_ABORT(tmp != oldSR,\n                     \"Unexpected use of X28 as a ScratchRegister\");\n  QBDI_REQUIRE_ABORT(tmp != nextSR,\n                     \"Unexpected use of X28 as a ScratchRegister\");\n\n  RelocatableInst::UniquePtrVec changeReloc;\n\n  changeReloc.push_back(RelocTag::unique(RelocTagChangeScratchRegister));\n\n  // load the real value of the old SR\n  changeReloc.push_back(NoReloc::unique(\n      ldr(tmp, oldSR, offsetof(Context, hostState.scratchRegisterValue))));\n  // backup the real value of the next SR\n  changeReloc.push_back(NoReloc::unique(\n      str(nextSR, oldSR, offsetof(Context, hostState.scratchRegisterValue))));\n  // change the SR\n  changeReloc.push_back(NoReloc::unique(movrr(nextSR, oldSR)));\n  // restore the value of the old SR\n  changeReloc.push_back(NoReloc::unique(movrr(oldSR, tmp)));\n  // change the index of the SRregister\n  changeReloc.push_back(NoReloc::unique(movri(tmp, nextSR.getID())));\n  changeReloc.push_back(NoReloc::unique(\n      str(tmp, nextSR, offsetof(Context, hostState.currentSROffset))));\n\n  return changeReloc;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/ExecBlockPatch_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef EXECBLOCKPATCH_AARCH64_H\n#define EXECBLOCKPATCH_AARCH64_H\n\n#include <memory>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nclass RelocatableInst;\nclass LLVMCPU;\n\nstd::vector<std::unique_ptr<RelocatableInst>>\nchangeScratchRegister(const LLVMCPU &llvmcpu, RegLLVM oldSR, RegLLVM nextSR);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/InstInfo_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <set>\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"AArch64InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n\n#include \"devVariable.h\"\n#include \"Patch/AARCH64/InstInfo_AARCH64.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n\n// TODO:\n// st2\t{ v0.4s, v1.4s }, [x16] -> ST2Twov4s\n// ld2\t{ v2.4s, v3.4s }, [x17] -> LD2Twov4s\n// etc\n\nnamespace QBDI {\nnamespace {\n\n// Read Instructions\n// =================\n\nconstexpr unsigned READ_8_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASAB,\n    llvm::AArch64::CASALB,\n    llvm::AArch64::CASB,\n    llvm::AArch64::CASLB,\n    llvm::AArch64::LD1Rv16b,\n    llvm::AArch64::LD1Rv16b_POST,\n    llvm::AArch64::LD1Rv8b,\n    llvm::AArch64::LD1Rv8b_POST,\n    llvm::AArch64::LD1i8,\n    llvm::AArch64::LD1i8_POST,\n    llvm::AArch64::LDADDAB,\n    llvm::AArch64::LDADDALB,\n    llvm::AArch64::LDADDB,\n    llvm::AArch64::LDADDLB,\n    llvm::AArch64::LDAPRB,\n    llvm::AArch64::LDAPURBi,\n    llvm::AArch64::LDAPURSBWi,\n    llvm::AArch64::LDAPURSBXi,\n    llvm::AArch64::LDAPURbi,\n    llvm::AArch64::LDARB,\n    llvm::AArch64::LDAXRB,\n    llvm::AArch64::LDCLRAB,\n    llvm::AArch64::LDCLRALB,\n    llvm::AArch64::LDCLRB,\n    llvm::AArch64::LDCLRLB,\n    llvm::AArch64::LDEORAB,\n    llvm::AArch64::LDEORALB,\n    llvm::AArch64::LDEORB,\n    llvm::AArch64::LDEORLB,\n    llvm::AArch64::LDLARB,\n    llvm::AArch64::LDRBBpost,\n    llvm::AArch64::LDRBBpre,\n    llvm::AArch64::LDRBBroW,\n    llvm::AArch64::LDRBBroX,\n    llvm::AArch64::LDRBBui,\n    llvm::AArch64::LDRBpost,\n    llvm::AArch64::LDRBpre,\n    llvm::AArch64::LDRBroW,\n    llvm::AArch64::LDRBroX,\n    llvm::AArch64::LDRBui,\n    llvm::AArch64::LDRSBWpost,\n    llvm::AArch64::LDRSBWpre,\n    llvm::AArch64::LDRSBWroW,\n    llvm::AArch64::LDRSBWroX,\n    llvm::AArch64::LDRSBWui,\n    llvm::AArch64::LDRSBXpost,\n    llvm::AArch64::LDRSBXpre,\n    llvm::AArch64::LDRSBXroW,\n    llvm::AArch64::LDRSBXroX,\n    llvm::AArch64::LDRSBXui,\n    llvm::AArch64::LDSETAB,\n    llvm::AArch64::LDSETALB,\n    llvm::AArch64::LDSETB,\n    llvm::AArch64::LDSETLB,\n    llvm::AArch64::LDSMAXAB,\n    llvm::AArch64::LDSMAXALB,\n    llvm::AArch64::LDSMAXB,\n    llvm::AArch64::LDSMAXLB,\n    llvm::AArch64::LDSMINAB,\n    llvm::AArch64::LDSMINALB,\n    llvm::AArch64::LDSMINB,\n    llvm::AArch64::LDSMINLB,\n    llvm::AArch64::LDTRBi,\n    llvm::AArch64::LDTRSBWi,\n    llvm::AArch64::LDTRSBXi,\n    llvm::AArch64::LDUMAXAB,\n    llvm::AArch64::LDUMAXALB,\n    llvm::AArch64::LDUMAXB,\n    llvm::AArch64::LDUMAXLB,\n    llvm::AArch64::LDUMINAB,\n    llvm::AArch64::LDUMINALB,\n    llvm::AArch64::LDUMINB,\n    llvm::AArch64::LDUMINLB,\n    llvm::AArch64::LDURBBi,\n    llvm::AArch64::LDURBi,\n    llvm::AArch64::LDURSBWi,\n    llvm::AArch64::LDURSBXi,\n    llvm::AArch64::LDXRB,\n    llvm::AArch64::SWPAB,\n    llvm::AArch64::SWPALB,\n    llvm::AArch64::SWPB,\n    llvm::AArch64::SWPLB,\n    // clang-format on\n};\n\nconstexpr size_t READ_8_P1_SIZE = sizeof(READ_8_P1) / sizeof(unsigned);\n\nconstexpr unsigned READ_8_P2[] = {\n    // clang-format off\n    llvm::AArch64::LD2Rv16b,\n    llvm::AArch64::LD2Rv16b_POST,\n    llvm::AArch64::LD2Rv8b,\n    llvm::AArch64::LD2Rv8b_POST,\n    llvm::AArch64::LD2i8,\n    llvm::AArch64::LD2i8_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_8_P2_SIZE = sizeof(READ_8_P2) / sizeof(unsigned);\n\nconstexpr unsigned READ_8_P3[] = {\n    // clang-format off\n    llvm::AArch64::LD3Rv16b,\n    llvm::AArch64::LD3Rv16b_POST,\n    llvm::AArch64::LD3Rv8b,\n    llvm::AArch64::LD3Rv8b_POST,\n    llvm::AArch64::LD3i8,\n    llvm::AArch64::LD3i8_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_8_P3_SIZE = sizeof(READ_8_P3) / sizeof(unsigned);\n\nconstexpr unsigned READ_8_P4[] = {\n    // clang-format off\n    llvm::AArch64::LD4Rv16b,\n    llvm::AArch64::LD4Rv16b_POST,\n    llvm::AArch64::LD4Rv8b,\n    llvm::AArch64::LD4Rv8b_POST,\n    llvm::AArch64::LD4i8,\n    llvm::AArch64::LD4i8_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_8_P4_SIZE = sizeof(READ_8_P4) / sizeof(unsigned);\n\nconstexpr unsigned READ_16_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASAH,\n    llvm::AArch64::CASALH,\n    llvm::AArch64::CASH,\n    llvm::AArch64::CASLH,\n    llvm::AArch64::LD1Rv4h,\n    llvm::AArch64::LD1Rv4h_POST,\n    llvm::AArch64::LD1Rv8h,\n    llvm::AArch64::LD1Rv8h_POST,\n    llvm::AArch64::LD1i16,\n    llvm::AArch64::LD1i16_POST,\n    llvm::AArch64::LDADDAH,\n    llvm::AArch64::LDADDALH,\n    llvm::AArch64::LDADDH,\n    llvm::AArch64::LDADDLH,\n    llvm::AArch64::LDAPRH,\n    llvm::AArch64::LDAPURHi,\n    llvm::AArch64::LDAPURSHWi,\n    llvm::AArch64::LDAPURSHXi,\n    llvm::AArch64::LDAPURhi,\n    llvm::AArch64::LDARH,\n    llvm::AArch64::LDAXRH,\n    llvm::AArch64::LDCLRAH,\n    llvm::AArch64::LDCLRALH,\n    llvm::AArch64::LDCLRH,\n    llvm::AArch64::LDCLRLH,\n    llvm::AArch64::LDEORAH,\n    llvm::AArch64::LDEORALH,\n    llvm::AArch64::LDEORH,\n    llvm::AArch64::LDEORLH,\n    llvm::AArch64::LDLARH,\n    llvm::AArch64::LDRHHpost,\n    llvm::AArch64::LDRHHpre,\n    llvm::AArch64::LDRHHroW,\n    llvm::AArch64::LDRHHroX,\n    llvm::AArch64::LDRHHui,\n    llvm::AArch64::LDRHpost,\n    llvm::AArch64::LDRHpre,\n    llvm::AArch64::LDRHroW,\n    llvm::AArch64::LDRHroX,\n    llvm::AArch64::LDRHui,\n    llvm::AArch64::LDRSHWpost,\n    llvm::AArch64::LDRSHWpre,\n    llvm::AArch64::LDRSHWroW,\n    llvm::AArch64::LDRSHWroX,\n    llvm::AArch64::LDRSHWui,\n    llvm::AArch64::LDRSHXpost,\n    llvm::AArch64::LDRSHXpre,\n    llvm::AArch64::LDRSHXroW,\n    llvm::AArch64::LDRSHXroX,\n    llvm::AArch64::LDRSHXui,\n    llvm::AArch64::LDSETAH,\n    llvm::AArch64::LDSETALH,\n    llvm::AArch64::LDSETH,\n    llvm::AArch64::LDSETLH,\n    llvm::AArch64::LDSMAXAH,\n    llvm::AArch64::LDSMAXALH,\n    llvm::AArch64::LDSMAXH,\n    llvm::AArch64::LDSMAXLH,\n    llvm::AArch64::LDSMINAH,\n    llvm::AArch64::LDSMINALH,\n    llvm::AArch64::LDSMINH,\n    llvm::AArch64::LDSMINLH,\n    llvm::AArch64::LDTRHi,\n    llvm::AArch64::LDTRSHWi,\n    llvm::AArch64::LDTRSHXi,\n    llvm::AArch64::LDUMAXAH,\n    llvm::AArch64::LDUMAXALH,\n    llvm::AArch64::LDUMAXH,\n    llvm::AArch64::LDUMAXLH,\n    llvm::AArch64::LDUMINAH,\n    llvm::AArch64::LDUMINALH,\n    llvm::AArch64::LDUMINH,\n    llvm::AArch64::LDUMINLH,\n    llvm::AArch64::LDURHHi,\n    llvm::AArch64::LDURHi,\n    llvm::AArch64::LDURSHWi,\n    llvm::AArch64::LDURSHXi,\n    llvm::AArch64::LDXRH,\n    llvm::AArch64::SWPAH,\n    llvm::AArch64::SWPALH,\n    llvm::AArch64::SWPH,\n    llvm::AArch64::SWPLH,\n    // clang-format on\n};\n\nconstexpr size_t READ_16_P1_SIZE = sizeof(READ_16_P1) / sizeof(unsigned);\n\nconstexpr unsigned READ_16_P2[] = {\n    // clang-format off\n    llvm::AArch64::LD2Rv4h,\n    llvm::AArch64::LD2Rv4h_POST,\n    llvm::AArch64::LD2Rv8h,\n    llvm::AArch64::LD2Rv8h_POST,\n    llvm::AArch64::LD2i16,\n    llvm::AArch64::LD2i16_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_16_P2_SIZE = sizeof(READ_16_P2) / sizeof(unsigned);\n\nconstexpr unsigned READ_16_P3[] = {\n    // clang-format off\n    llvm::AArch64::LD3Rv4h,\n    llvm::AArch64::LD3Rv4h_POST,\n    llvm::AArch64::LD3Rv8h,\n    llvm::AArch64::LD3Rv8h_POST,\n    llvm::AArch64::LD3i16,\n    llvm::AArch64::LD3i16_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_16_P3_SIZE = sizeof(READ_16_P3) / sizeof(unsigned);\n\nconstexpr unsigned READ_16_P4[] = {\n    // clang-format off\n    llvm::AArch64::LD4Rv4h,\n    llvm::AArch64::LD4Rv4h_POST,\n    llvm::AArch64::LD4Rv8h,\n    llvm::AArch64::LD4Rv8h_POST,\n    llvm::AArch64::LD4i16,\n    llvm::AArch64::LD4i16_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_16_P4_SIZE = sizeof(READ_16_P4) / sizeof(unsigned);\n\nconstexpr unsigned READ_32_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASALW,\n    llvm::AArch64::CASAW,\n    llvm::AArch64::CASLW,\n    llvm::AArch64::CASW,\n    llvm::AArch64::LD1Rv2s,\n    llvm::AArch64::LD1Rv2s_POST,\n    llvm::AArch64::LD1Rv4s,\n    llvm::AArch64::LD1Rv4s_POST,\n    llvm::AArch64::LD1i32,\n    llvm::AArch64::LD1i32_POST,\n    llvm::AArch64::LDADDALW,\n    llvm::AArch64::LDADDAW,\n    llvm::AArch64::LDADDLW,\n    llvm::AArch64::LDADDW,\n    llvm::AArch64::LDAPRW,\n    llvm::AArch64::LDAPRWpost,\n    llvm::AArch64::LDAPURSWi,\n    llvm::AArch64::LDAPURi,\n    llvm::AArch64::LDAPURsi,\n    llvm::AArch64::LDARW,\n    llvm::AArch64::LDAXRW,\n    llvm::AArch64::LDCLRALW,\n    llvm::AArch64::LDCLRAW,\n    llvm::AArch64::LDCLRLW,\n    llvm::AArch64::LDCLRW,\n    llvm::AArch64::LDEORALW,\n    llvm::AArch64::LDEORAW,\n    llvm::AArch64::LDEORLW,\n    llvm::AArch64::LDEORW,\n    llvm::AArch64::LDLARW,\n    llvm::AArch64::LDRSWl,\n    llvm::AArch64::LDRSWpost,\n    llvm::AArch64::LDRSWpre,\n    llvm::AArch64::LDRSWroW,\n    llvm::AArch64::LDRSWroX,\n    llvm::AArch64::LDRSWui,\n    llvm::AArch64::LDRSl,\n    llvm::AArch64::LDRSpost,\n    llvm::AArch64::LDRSpre,\n    llvm::AArch64::LDRSroW,\n    llvm::AArch64::LDRSroX,\n    llvm::AArch64::LDRSui,\n    llvm::AArch64::LDRWl,\n    llvm::AArch64::LDRWpost,\n    llvm::AArch64::LDRWpre,\n    llvm::AArch64::LDRWroW,\n    llvm::AArch64::LDRWroX,\n    llvm::AArch64::LDRWui,\n    llvm::AArch64::LDSETALW,\n    llvm::AArch64::LDSETAW,\n    llvm::AArch64::LDSETLW,\n    llvm::AArch64::LDSETW,\n    llvm::AArch64::LDSMAXALW,\n    llvm::AArch64::LDSMAXAW,\n    llvm::AArch64::LDSMAXLW,\n    llvm::AArch64::LDSMAXW,\n    llvm::AArch64::LDSMINALW,\n    llvm::AArch64::LDSMINAW,\n    llvm::AArch64::LDSMINLW,\n    llvm::AArch64::LDSMINW,\n    llvm::AArch64::LDTRSWi,\n    llvm::AArch64::LDTRWi,\n    llvm::AArch64::LDUMAXALW,\n    llvm::AArch64::LDUMAXAW,\n    llvm::AArch64::LDUMAXLW,\n    llvm::AArch64::LDUMAXW,\n    llvm::AArch64::LDUMINALW,\n    llvm::AArch64::LDUMINAW,\n    llvm::AArch64::LDUMINLW,\n    llvm::AArch64::LDUMINW,\n    llvm::AArch64::LDURSWi,\n    llvm::AArch64::LDURSi,\n    llvm::AArch64::LDURWi,\n    llvm::AArch64::LDXRW,\n    llvm::AArch64::SWPALW,\n    llvm::AArch64::SWPAW,\n    llvm::AArch64::SWPLW,\n    llvm::AArch64::SWPW,\n    // clang-format on\n};\n\nconstexpr size_t READ_32_P1_SIZE = sizeof(READ_32_P1) / sizeof(unsigned);\n\nconstexpr unsigned READ_32_P2[] = {\n    // clang-format off\n    llvm::AArch64::CASPALW,\n    llvm::AArch64::CASPAW,\n    llvm::AArch64::CASPLW,\n    llvm::AArch64::CASPW,\n    llvm::AArch64::LD2Rv2s,\n    llvm::AArch64::LD2Rv2s_POST,\n    llvm::AArch64::LD2Rv4s,\n    llvm::AArch64::LD2Rv4s_POST,\n    llvm::AArch64::LD2i32,\n    llvm::AArch64::LD2i32_POST,\n    llvm::AArch64::LDAXPW,\n    llvm::AArch64::LDIAPPW,\n    llvm::AArch64::LDIAPPWpost,\n    llvm::AArch64::LDNPSi,\n    llvm::AArch64::LDNPWi,\n    llvm::AArch64::LDPSWi,\n    llvm::AArch64::LDPSWpost,\n    llvm::AArch64::LDPSWpre,\n    llvm::AArch64::LDPSi,\n    llvm::AArch64::LDPSpost,\n    llvm::AArch64::LDPSpre,\n    llvm::AArch64::LDPWi,\n    llvm::AArch64::LDPWpost,\n    llvm::AArch64::LDPWpre,\n    llvm::AArch64::LDXPW,\n    // clang-format on\n};\n\nconstexpr size_t READ_32_P2_SIZE = sizeof(READ_32_P2) / sizeof(unsigned);\n\nconstexpr unsigned READ_32_P3[] = {\n    // clang-format off\n    llvm::AArch64::LD3Rv2s,\n    llvm::AArch64::LD3Rv2s_POST,\n    llvm::AArch64::LD3Rv4s,\n    llvm::AArch64::LD3Rv4s_POST,\n    llvm::AArch64::LD3i32,\n    llvm::AArch64::LD3i32_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_32_P3_SIZE = sizeof(READ_32_P3) / sizeof(unsigned);\n\nconstexpr unsigned READ_32_P4[] = {\n    // clang-format off\n    llvm::AArch64::LD4Rv2s,\n    llvm::AArch64::LD4Rv2s_POST,\n    llvm::AArch64::LD4Rv4s,\n    llvm::AArch64::LD4Rv4s_POST,\n    llvm::AArch64::LD4i32,\n    llvm::AArch64::LD4i32_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_32_P4_SIZE = sizeof(READ_32_P4) / sizeof(unsigned);\n\nconstexpr unsigned READ_64_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASALX,\n    llvm::AArch64::CASAX,\n    llvm::AArch64::CASLX,\n    llvm::AArch64::CASX,\n    llvm::AArch64::LD1Onev1d,\n    llvm::AArch64::LD1Onev1d_POST,\n    llvm::AArch64::LD1Onev2s,\n    llvm::AArch64::LD1Onev2s_POST,\n    llvm::AArch64::LD1Onev4h,\n    llvm::AArch64::LD1Onev4h_POST,\n    llvm::AArch64::LD1Onev8b,\n    llvm::AArch64::LD1Onev8b_POST,\n    llvm::AArch64::LD1Rv1d,\n    llvm::AArch64::LD1Rv1d_POST,\n    llvm::AArch64::LD1Rv2d,\n    llvm::AArch64::LD1Rv2d_POST,\n    llvm::AArch64::LD1i64,\n    llvm::AArch64::LD1i64_POST,\n    llvm::AArch64::LDADDALX,\n    llvm::AArch64::LDADDAX,\n    llvm::AArch64::LDADDLX,\n    llvm::AArch64::LDADDX,\n    llvm::AArch64::LDAP1,\n    llvm::AArch64::LDAPRX,\n    llvm::AArch64::LDAPRXpost,\n    llvm::AArch64::LDAPURXi,\n    llvm::AArch64::LDAPURdi,\n    llvm::AArch64::LDARX,\n    llvm::AArch64::LDAXRX,\n    llvm::AArch64::LDCLRALX,\n    llvm::AArch64::LDCLRAX,\n    llvm::AArch64::LDCLRLX,\n    llvm::AArch64::LDCLRX,\n    llvm::AArch64::LDEORALX,\n    llvm::AArch64::LDEORAX,\n    llvm::AArch64::LDEORLX,\n    llvm::AArch64::LDEORX,\n    llvm::AArch64::LDLARX,\n    llvm::AArch64::LDRAAindexed,\n    llvm::AArch64::LDRAAwriteback,\n    llvm::AArch64::LDRABindexed,\n    llvm::AArch64::LDRABwriteback,\n    llvm::AArch64::LDRDl,\n    llvm::AArch64::LDRDpost,\n    llvm::AArch64::LDRDpre,\n    llvm::AArch64::LDRDroW,\n    llvm::AArch64::LDRDroX,\n    llvm::AArch64::LDRDui,\n    llvm::AArch64::LDRXl,\n    llvm::AArch64::LDRXpost,\n    llvm::AArch64::LDRXpre,\n    llvm::AArch64::LDRXroW,\n    llvm::AArch64::LDRXroX,\n    llvm::AArch64::LDRXui,\n    llvm::AArch64::LDSETALX,\n    llvm::AArch64::LDSETAX,\n    llvm::AArch64::LDSETLX,\n    llvm::AArch64::LDSETX,\n    llvm::AArch64::LDSMAXALX,\n    llvm::AArch64::LDSMAXAX,\n    llvm::AArch64::LDSMAXLX,\n    llvm::AArch64::LDSMAXX,\n    llvm::AArch64::LDSMINALX,\n    llvm::AArch64::LDSMINAX,\n    llvm::AArch64::LDSMINLX,\n    llvm::AArch64::LDSMINX,\n    llvm::AArch64::LDTRXi,\n    llvm::AArch64::LDUMAXALX,\n    llvm::AArch64::LDUMAXAX,\n    llvm::AArch64::LDUMAXLX,\n    llvm::AArch64::LDUMAXX,\n    llvm::AArch64::LDUMINALX,\n    llvm::AArch64::LDUMINAX,\n    llvm::AArch64::LDUMINLX,\n    llvm::AArch64::LDUMINX,\n    llvm::AArch64::LDURDi,\n    llvm::AArch64::LDURXi,\n    llvm::AArch64::LDXRX,\n    llvm::AArch64::RCWCAS,\n    llvm::AArch64::RCWCASA,\n    llvm::AArch64::RCWCASAL,\n    llvm::AArch64::RCWCASL,\n    llvm::AArch64::RCWCLR,\n    llvm::AArch64::RCWCLRA,\n    llvm::AArch64::RCWCLRAL,\n    llvm::AArch64::RCWCLRL,\n    llvm::AArch64::RCWCLRS,\n    llvm::AArch64::RCWCLRSA,\n    llvm::AArch64::RCWCLRSAL,\n    llvm::AArch64::RCWCLRSL,\n    llvm::AArch64::RCWSCAS,\n    llvm::AArch64::RCWSCASA,\n    llvm::AArch64::RCWSCASAL,\n    llvm::AArch64::RCWSCASL,\n    llvm::AArch64::RCWSET,\n    llvm::AArch64::RCWSETA,\n    llvm::AArch64::RCWSETAL,\n    llvm::AArch64::RCWSETL,\n    llvm::AArch64::RCWSETS,\n    llvm::AArch64::RCWSETSA,\n    llvm::AArch64::RCWSETSAL,\n    llvm::AArch64::RCWSETSL,\n    llvm::AArch64::RCWSWP,\n    llvm::AArch64::RCWSWPA,\n    llvm::AArch64::RCWSWPAL,\n    llvm::AArch64::RCWSWPL,\n    llvm::AArch64::RCWSWPS,\n    llvm::AArch64::RCWSWPSA,\n    llvm::AArch64::RCWSWPSAL,\n    llvm::AArch64::RCWSWPSL,\n    llvm::AArch64::SWPALX,\n    llvm::AArch64::SWPAX,\n    llvm::AArch64::SWPLX,\n    llvm::AArch64::SWPX,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_P1_SIZE = sizeof(READ_64_P1) / sizeof(unsigned);\n\nconstexpr unsigned READ_64_P2[] = {\n    // clang-format off\n    llvm::AArch64::CASPALX,\n    llvm::AArch64::CASPAX,\n    llvm::AArch64::CASPLX,\n    llvm::AArch64::CASPX,\n    llvm::AArch64::LD1Twov1d,\n    llvm::AArch64::LD1Twov1d_POST,\n    llvm::AArch64::LD1Twov2s,\n    llvm::AArch64::LD1Twov2s_POST,\n    llvm::AArch64::LD1Twov4h,\n    llvm::AArch64::LD1Twov4h_POST,\n    llvm::AArch64::LD1Twov8b,\n    llvm::AArch64::LD1Twov8b_POST,\n    llvm::AArch64::LD2Rv1d,\n    llvm::AArch64::LD2Rv1d_POST,\n    llvm::AArch64::LD2Rv2d,\n    llvm::AArch64::LD2Rv2d_POST,\n    llvm::AArch64::LD2Twov2s,\n    llvm::AArch64::LD2Twov2s_POST,\n    llvm::AArch64::LD2Twov4h,\n    llvm::AArch64::LD2Twov4h_POST,\n    llvm::AArch64::LD2Twov8b,\n    llvm::AArch64::LD2Twov8b_POST,\n    llvm::AArch64::LD2i64,\n    llvm::AArch64::LD2i64_POST,\n    llvm::AArch64::LDAXPX,\n    llvm::AArch64::LDIAPPX,\n    llvm::AArch64::LDIAPPXpost,\n    llvm::AArch64::LDNPDi,\n    llvm::AArch64::LDNPXi,\n    llvm::AArch64::LDPDi,\n    llvm::AArch64::LDPDpost,\n    llvm::AArch64::LDPDpre,\n    llvm::AArch64::LDPXi,\n    llvm::AArch64::LDPXpost,\n    llvm::AArch64::LDPXpre,\n    llvm::AArch64::LDXPX,\n    llvm::AArch64::RCWCASP,\n    llvm::AArch64::RCWCASPA,\n    llvm::AArch64::RCWCASPAL,\n    llvm::AArch64::RCWCASPL,\n    llvm::AArch64::RCWCLRP,\n    llvm::AArch64::RCWCLRPA,\n    llvm::AArch64::RCWCLRPAL,\n    llvm::AArch64::RCWCLRPL,\n    llvm::AArch64::RCWCLRSP,\n    llvm::AArch64::RCWCLRSPA,\n    llvm::AArch64::RCWCLRSPAL,\n    llvm::AArch64::RCWCLRSPL,\n    llvm::AArch64::RCWSCASP,\n    llvm::AArch64::RCWSCASPA,\n    llvm::AArch64::RCWSCASPAL,\n    llvm::AArch64::RCWSCASPL,\n    llvm::AArch64::RCWSETP,\n    llvm::AArch64::RCWSETPA,\n    llvm::AArch64::RCWSETPAL,\n    llvm::AArch64::RCWSETPL,\n    llvm::AArch64::RCWSETSP,\n    llvm::AArch64::RCWSETSPA,\n    llvm::AArch64::RCWSETSPAL,\n    llvm::AArch64::RCWSETSPL,\n    llvm::AArch64::RCWSWPP,\n    llvm::AArch64::RCWSWPPA,\n    llvm::AArch64::RCWSWPPAL,\n    llvm::AArch64::RCWSWPPL,\n    llvm::AArch64::RCWSWPSP,\n    llvm::AArch64::RCWSWPSPA,\n    llvm::AArch64::RCWSWPSPAL,\n    llvm::AArch64::RCWSWPSPL,\n    llvm::AArch64::SWPP,\n    llvm::AArch64::SWPPA,\n    llvm::AArch64::SWPPAL,\n    llvm::AArch64::SWPPL,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_P2_SIZE = sizeof(READ_64_P2) / sizeof(unsigned);\n\nconstexpr unsigned READ_64_P3[] = {\n    // clang-format off\n    llvm::AArch64::LD1Threev1d,\n    llvm::AArch64::LD1Threev1d_POST,\n    llvm::AArch64::LD1Threev2s,\n    llvm::AArch64::LD1Threev2s_POST,\n    llvm::AArch64::LD1Threev4h,\n    llvm::AArch64::LD1Threev4h_POST,\n    llvm::AArch64::LD1Threev8b,\n    llvm::AArch64::LD1Threev8b_POST,\n    llvm::AArch64::LD3Rv1d,\n    llvm::AArch64::LD3Rv1d_POST,\n    llvm::AArch64::LD3Rv2d,\n    llvm::AArch64::LD3Rv2d_POST,\n    llvm::AArch64::LD3Threev2s,\n    llvm::AArch64::LD3Threev2s_POST,\n    llvm::AArch64::LD3Threev4h,\n    llvm::AArch64::LD3Threev4h_POST,\n    llvm::AArch64::LD3Threev8b,\n    llvm::AArch64::LD3Threev8b_POST,\n    llvm::AArch64::LD3i64,\n    llvm::AArch64::LD3i64_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_P3_SIZE = sizeof(READ_64_P3) / sizeof(unsigned);\n\nconstexpr unsigned READ_64_P4[] = {\n    // clang-format off\n    llvm::AArch64::LD1Fourv1d,\n    llvm::AArch64::LD1Fourv1d_POST,\n    llvm::AArch64::LD1Fourv2s,\n    llvm::AArch64::LD1Fourv2s_POST,\n    llvm::AArch64::LD1Fourv4h,\n    llvm::AArch64::LD1Fourv4h_POST,\n    llvm::AArch64::LD1Fourv8b,\n    llvm::AArch64::LD1Fourv8b_POST,\n    llvm::AArch64::LD4Fourv2s,\n    llvm::AArch64::LD4Fourv2s_POST,\n    llvm::AArch64::LD4Fourv4h,\n    llvm::AArch64::LD4Fourv4h_POST,\n    llvm::AArch64::LD4Fourv8b,\n    llvm::AArch64::LD4Fourv8b_POST,\n    llvm::AArch64::LD4Rv1d,\n    llvm::AArch64::LD4Rv1d_POST,\n    llvm::AArch64::LD4Rv2d,\n    llvm::AArch64::LD4Rv2d_POST,\n    llvm::AArch64::LD4i64,\n    llvm::AArch64::LD4i64_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_P4_SIZE = sizeof(READ_64_P4) / sizeof(unsigned);\n\nconstexpr unsigned READ_64_P8[] = {\n    // clang-format off\n    llvm::AArch64::LD64B,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_P8_SIZE = sizeof(READ_64_P8) / sizeof(unsigned);\n\nconstexpr unsigned READ_128_P1[] = {\n    // clang-format off\n    llvm::AArch64::LD1Onev16b,\n    llvm::AArch64::LD1Onev16b_POST,\n    llvm::AArch64::LD1Onev2d,\n    llvm::AArch64::LD1Onev2d_POST,\n    llvm::AArch64::LD1Onev4s,\n    llvm::AArch64::LD1Onev4s_POST,\n    llvm::AArch64::LD1Onev8h,\n    llvm::AArch64::LD1Onev8h_POST,\n    llvm::AArch64::LDAPURqi,\n    llvm::AArch64::LDCLRP,\n    llvm::AArch64::LDCLRPA,\n    llvm::AArch64::LDCLRPAL,\n    llvm::AArch64::LDCLRPL,\n    llvm::AArch64::LDRQl,\n    llvm::AArch64::LDRQpost,\n    llvm::AArch64::LDRQpre,\n    llvm::AArch64::LDRQroW,\n    llvm::AArch64::LDRQroX,\n    llvm::AArch64::LDRQui,\n    llvm::AArch64::LDSETP,\n    llvm::AArch64::LDSETPA,\n    llvm::AArch64::LDSETPAL,\n    llvm::AArch64::LDSETPL,\n    llvm::AArch64::LDURQi,\n    // clang-format on\n};\n\nconstexpr size_t READ_128_P1_SIZE = sizeof(READ_128_P1) / sizeof(unsigned);\n\nconstexpr unsigned READ_128_P2[] = {\n    // clang-format off\n    llvm::AArch64::LD1Twov16b,\n    llvm::AArch64::LD1Twov16b_POST,\n    llvm::AArch64::LD1Twov2d,\n    llvm::AArch64::LD1Twov2d_POST,\n    llvm::AArch64::LD1Twov4s,\n    llvm::AArch64::LD1Twov4s_POST,\n    llvm::AArch64::LD1Twov8h,\n    llvm::AArch64::LD1Twov8h_POST,\n    llvm::AArch64::LD2Twov16b,\n    llvm::AArch64::LD2Twov16b_POST,\n    llvm::AArch64::LD2Twov2d,\n    llvm::AArch64::LD2Twov2d_POST,\n    llvm::AArch64::LD2Twov4s,\n    llvm::AArch64::LD2Twov4s_POST,\n    llvm::AArch64::LD2Twov8h,\n    llvm::AArch64::LD2Twov8h_POST,\n    llvm::AArch64::LDNPQi,\n    llvm::AArch64::LDPQi,\n    llvm::AArch64::LDPQpost,\n    llvm::AArch64::LDPQpre,\n    // clang-format on\n};\n\nconstexpr size_t READ_128_P2_SIZE = sizeof(READ_128_P2) / sizeof(unsigned);\n\nconstexpr unsigned READ_128_P3[] = {\n    // clang-format off\n    llvm::AArch64::LD1Threev16b,\n    llvm::AArch64::LD1Threev16b_POST,\n    llvm::AArch64::LD1Threev2d,\n    llvm::AArch64::LD1Threev2d_POST,\n    llvm::AArch64::LD1Threev4s,\n    llvm::AArch64::LD1Threev4s_POST,\n    llvm::AArch64::LD1Threev8h,\n    llvm::AArch64::LD1Threev8h_POST,\n    llvm::AArch64::LD3Threev16b,\n    llvm::AArch64::LD3Threev16b_POST,\n    llvm::AArch64::LD3Threev2d,\n    llvm::AArch64::LD3Threev2d_POST,\n    llvm::AArch64::LD3Threev4s,\n    llvm::AArch64::LD3Threev4s_POST,\n    llvm::AArch64::LD3Threev8h,\n    llvm::AArch64::LD3Threev8h_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_128_P3_SIZE = sizeof(READ_128_P3) / sizeof(unsigned);\n\nconstexpr unsigned READ_128_P4[] = {\n    // clang-format off\n    llvm::AArch64::LD1Fourv16b,\n    llvm::AArch64::LD1Fourv16b_POST,\n    llvm::AArch64::LD1Fourv2d,\n    llvm::AArch64::LD1Fourv2d_POST,\n    llvm::AArch64::LD1Fourv4s,\n    llvm::AArch64::LD1Fourv4s_POST,\n    llvm::AArch64::LD1Fourv8h,\n    llvm::AArch64::LD1Fourv8h_POST,\n    llvm::AArch64::LD4Fourv16b,\n    llvm::AArch64::LD4Fourv16b_POST,\n    llvm::AArch64::LD4Fourv2d,\n    llvm::AArch64::LD4Fourv2d_POST,\n    llvm::AArch64::LD4Fourv4s,\n    llvm::AArch64::LD4Fourv4s_POST,\n    llvm::AArch64::LD4Fourv8h,\n    llvm::AArch64::LD4Fourv8h_POST,\n    // clang-format on\n};\n\nconstexpr size_t READ_128_P4_SIZE = sizeof(READ_128_P4) / sizeof(unsigned);\n\nconstexpr unsigned READ_DYN[] = {\n    // clang-format off\n    llvm::AArch64::CPYE,\n    llvm::AArch64::CPYEN,\n    llvm::AArch64::CPYERN,\n    llvm::AArch64::CPYERT,\n    llvm::AArch64::CPYERTN,\n    llvm::AArch64::CPYERTRN,\n    llvm::AArch64::CPYERTWN,\n    llvm::AArch64::CPYET,\n    llvm::AArch64::CPYETN,\n    llvm::AArch64::CPYETRN,\n    llvm::AArch64::CPYETWN,\n    llvm::AArch64::CPYEWN,\n    llvm::AArch64::CPYEWT,\n    llvm::AArch64::CPYEWTN,\n    llvm::AArch64::CPYEWTRN,\n    llvm::AArch64::CPYEWTWN,\n    llvm::AArch64::CPYFE,\n    llvm::AArch64::CPYFEN,\n    llvm::AArch64::CPYFERN,\n    llvm::AArch64::CPYFERT,\n    llvm::AArch64::CPYFERTN,\n    llvm::AArch64::CPYFERTRN,\n    llvm::AArch64::CPYFERTWN,\n    llvm::AArch64::CPYFET,\n    llvm::AArch64::CPYFETN,\n    llvm::AArch64::CPYFETRN,\n    llvm::AArch64::CPYFETWN,\n    llvm::AArch64::CPYFEWN,\n    llvm::AArch64::CPYFEWT,\n    llvm::AArch64::CPYFEWTN,\n    llvm::AArch64::CPYFEWTRN,\n    llvm::AArch64::CPYFEWTWN,\n    llvm::AArch64::CPYFM,\n    llvm::AArch64::CPYFMN,\n    llvm::AArch64::CPYFMRN,\n    llvm::AArch64::CPYFMRT,\n    llvm::AArch64::CPYFMRTN,\n    llvm::AArch64::CPYFMRTRN,\n    llvm::AArch64::CPYFMRTWN,\n    llvm::AArch64::CPYFMT,\n    llvm::AArch64::CPYFMTN,\n    llvm::AArch64::CPYFMTRN,\n    llvm::AArch64::CPYFMTWN,\n    llvm::AArch64::CPYFMWN,\n    llvm::AArch64::CPYFMWT,\n    llvm::AArch64::CPYFMWTN,\n    llvm::AArch64::CPYFMWTRN,\n    llvm::AArch64::CPYFMWTWN,\n    llvm::AArch64::CPYFP,\n    llvm::AArch64::CPYFPN,\n    llvm::AArch64::CPYFPRN,\n    llvm::AArch64::CPYFPRT,\n    llvm::AArch64::CPYFPRTN,\n    llvm::AArch64::CPYFPRTRN,\n    llvm::AArch64::CPYFPRTWN,\n    llvm::AArch64::CPYFPT,\n    llvm::AArch64::CPYFPTN,\n    llvm::AArch64::CPYFPTRN,\n    llvm::AArch64::CPYFPTWN,\n    llvm::AArch64::CPYFPWN,\n    llvm::AArch64::CPYFPWT,\n    llvm::AArch64::CPYFPWTN,\n    llvm::AArch64::CPYFPWTRN,\n    llvm::AArch64::CPYFPWTWN,\n    llvm::AArch64::CPYM,\n    llvm::AArch64::CPYMN,\n    llvm::AArch64::CPYMRN,\n    llvm::AArch64::CPYMRT,\n    llvm::AArch64::CPYMRTN,\n    llvm::AArch64::CPYMRTRN,\n    llvm::AArch64::CPYMRTWN,\n    llvm::AArch64::CPYMT,\n    llvm::AArch64::CPYMTN,\n    llvm::AArch64::CPYMTRN,\n    llvm::AArch64::CPYMTWN,\n    llvm::AArch64::CPYMWN,\n    llvm::AArch64::CPYMWT,\n    llvm::AArch64::CPYMWTN,\n    llvm::AArch64::CPYMWTRN,\n    llvm::AArch64::CPYMWTWN,\n    llvm::AArch64::CPYP,\n    llvm::AArch64::CPYPN,\n    llvm::AArch64::CPYPRN,\n    llvm::AArch64::CPYPRT,\n    llvm::AArch64::CPYPRTN,\n    llvm::AArch64::CPYPRTRN,\n    llvm::AArch64::CPYPRTWN,\n    llvm::AArch64::CPYPT,\n    llvm::AArch64::CPYPTN,\n    llvm::AArch64::CPYPTRN,\n    llvm::AArch64::CPYPTWN,\n    llvm::AArch64::CPYPWN,\n    llvm::AArch64::CPYPWT,\n    llvm::AArch64::CPYPWTN,\n    llvm::AArch64::CPYPWTRN,\n    llvm::AArch64::CPYPWTWN,\n    // clang-format on\n};\n\nconstexpr size_t READ_DYN_SIZE = sizeof(READ_DYN) / sizeof(unsigned);\n\n// Write Instructions\n// ==================\nconstexpr unsigned WRITE_8_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASAB,\n    llvm::AArch64::CASALB,\n    llvm::AArch64::CASB,\n    llvm::AArch64::CASLB,\n    llvm::AArch64::LDADDAB,\n    llvm::AArch64::LDADDALB,\n    llvm::AArch64::LDADDB,\n    llvm::AArch64::LDADDLB,\n    llvm::AArch64::LDCLRAB,\n    llvm::AArch64::LDCLRALB,\n    llvm::AArch64::LDCLRB,\n    llvm::AArch64::LDCLRLB,\n    llvm::AArch64::LDEORAB,\n    llvm::AArch64::LDEORALB,\n    llvm::AArch64::LDEORB,\n    llvm::AArch64::LDEORLB,\n    llvm::AArch64::LDSETAB,\n    llvm::AArch64::LDSETALB,\n    llvm::AArch64::LDSETB,\n    llvm::AArch64::LDSETLB,\n    llvm::AArch64::LDSMAXAB,\n    llvm::AArch64::LDSMAXALB,\n    llvm::AArch64::LDSMAXB,\n    llvm::AArch64::LDSMAXLB,\n    llvm::AArch64::LDSMINAB,\n    llvm::AArch64::LDSMINALB,\n    llvm::AArch64::LDSMINB,\n    llvm::AArch64::LDSMINLB,\n    llvm::AArch64::LDUMAXAB,\n    llvm::AArch64::LDUMAXALB,\n    llvm::AArch64::LDUMAXB,\n    llvm::AArch64::LDUMAXLB,\n    llvm::AArch64::LDUMINAB,\n    llvm::AArch64::LDUMINALB,\n    llvm::AArch64::LDUMINB,\n    llvm::AArch64::LDUMINLB,\n    llvm::AArch64::ST1i8,\n    llvm::AArch64::ST1i8_POST,\n    llvm::AArch64::STLLRB,\n    llvm::AArch64::STLRB,\n    llvm::AArch64::STLURBi,\n    llvm::AArch64::STLURbi,\n    llvm::AArch64::STLXRB,\n    llvm::AArch64::STRBBpost,\n    llvm::AArch64::STRBBpre,\n    llvm::AArch64::STRBBroW,\n    llvm::AArch64::STRBBroX,\n    llvm::AArch64::STRBBui,\n    llvm::AArch64::STRBpost,\n    llvm::AArch64::STRBpre,\n    llvm::AArch64::STRBroW,\n    llvm::AArch64::STRBroX,\n    llvm::AArch64::STRBui,\n    llvm::AArch64::STTRBi,\n    llvm::AArch64::STURBBi,\n    llvm::AArch64::STURBi,\n    llvm::AArch64::STXRB,\n    llvm::AArch64::SWPAB,\n    llvm::AArch64::SWPALB,\n    llvm::AArch64::SWPB,\n    llvm::AArch64::SWPLB,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_8_P1_SIZE = sizeof(WRITE_8_P1) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_8_P2[] = {\n    // clang-format off\n    llvm::AArch64::ST2i8,\n    llvm::AArch64::ST2i8_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_8_P2_SIZE = sizeof(WRITE_8_P2) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_8_P3[] = {\n    // clang-format off\n    llvm::AArch64::ST3i8,\n    llvm::AArch64::ST3i8_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_8_P3_SIZE = sizeof(WRITE_8_P3) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_8_P4[] = {\n    // clang-format off\n    llvm::AArch64::ST4i8,\n    llvm::AArch64::ST4i8_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_8_P4_SIZE = sizeof(WRITE_8_P4) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_16_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASAH,\n    llvm::AArch64::CASALH,\n    llvm::AArch64::CASH,\n    llvm::AArch64::CASLH,\n    llvm::AArch64::LDADDAH,\n    llvm::AArch64::LDADDALH,\n    llvm::AArch64::LDADDH,\n    llvm::AArch64::LDADDLH,\n    llvm::AArch64::LDCLRAH,\n    llvm::AArch64::LDCLRALH,\n    llvm::AArch64::LDCLRH,\n    llvm::AArch64::LDCLRLH,\n    llvm::AArch64::LDEORAH,\n    llvm::AArch64::LDEORALH,\n    llvm::AArch64::LDEORH,\n    llvm::AArch64::LDEORLH,\n    llvm::AArch64::LDSETAH,\n    llvm::AArch64::LDSETALH,\n    llvm::AArch64::LDSETH,\n    llvm::AArch64::LDSETLH,\n    llvm::AArch64::LDSMAXAH,\n    llvm::AArch64::LDSMAXALH,\n    llvm::AArch64::LDSMAXH,\n    llvm::AArch64::LDSMAXLH,\n    llvm::AArch64::LDSMINAH,\n    llvm::AArch64::LDSMINALH,\n    llvm::AArch64::LDSMINH,\n    llvm::AArch64::LDSMINLH,\n    llvm::AArch64::LDUMAXAH,\n    llvm::AArch64::LDUMAXALH,\n    llvm::AArch64::LDUMAXH,\n    llvm::AArch64::LDUMAXLH,\n    llvm::AArch64::LDUMINAH,\n    llvm::AArch64::LDUMINALH,\n    llvm::AArch64::LDUMINH,\n    llvm::AArch64::LDUMINLH,\n    llvm::AArch64::ST1i16,\n    llvm::AArch64::ST1i16_POST,\n    llvm::AArch64::STLLRH,\n    llvm::AArch64::STLRH,\n    llvm::AArch64::STLURHi,\n    llvm::AArch64::STLURhi,\n    llvm::AArch64::STLXRH,\n    llvm::AArch64::STRHHpost,\n    llvm::AArch64::STRHHpre,\n    llvm::AArch64::STRHHroW,\n    llvm::AArch64::STRHHroX,\n    llvm::AArch64::STRHHui,\n    llvm::AArch64::STRHpost,\n    llvm::AArch64::STRHpre,\n    llvm::AArch64::STRHroW,\n    llvm::AArch64::STRHroX,\n    llvm::AArch64::STRHui,\n    llvm::AArch64::STTRHi,\n    llvm::AArch64::STURHHi,\n    llvm::AArch64::STURHi,\n    llvm::AArch64::STXRH,\n    llvm::AArch64::SWPAH,\n    llvm::AArch64::SWPALH,\n    llvm::AArch64::SWPH,\n    llvm::AArch64::SWPLH,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_16_P1_SIZE = sizeof(WRITE_16_P1) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_16_P2[] = {\n    // clang-format off\n    llvm::AArch64::ST2i16,\n    llvm::AArch64::ST2i16_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_16_P2_SIZE = sizeof(WRITE_16_P2) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_16_P3[] = {\n    // clang-format off\n    llvm::AArch64::ST3i16,\n    llvm::AArch64::ST3i16_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_16_P3_SIZE = sizeof(WRITE_16_P3) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_16_P4[] = {\n    // clang-format off\n    llvm::AArch64::ST4i16,\n    llvm::AArch64::ST4i16_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_16_P4_SIZE = sizeof(WRITE_16_P4) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_32_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASALW,\n    llvm::AArch64::CASAW,\n    llvm::AArch64::CASLW,\n    llvm::AArch64::CASW,\n    llvm::AArch64::LDADDALW,\n    llvm::AArch64::LDADDAW,\n    llvm::AArch64::LDADDLW,\n    llvm::AArch64::LDADDW,\n    llvm::AArch64::LDCLRALW,\n    llvm::AArch64::LDCLRAW,\n    llvm::AArch64::LDCLRLW,\n    llvm::AArch64::LDCLRW,\n    llvm::AArch64::LDEORALW,\n    llvm::AArch64::LDEORAW,\n    llvm::AArch64::LDEORLW,\n    llvm::AArch64::LDEORW,\n    llvm::AArch64::LDSETALW,\n    llvm::AArch64::LDSETAW,\n    llvm::AArch64::LDSETLW,\n    llvm::AArch64::LDSETW,\n    llvm::AArch64::LDSMAXALW,\n    llvm::AArch64::LDSMAXAW,\n    llvm::AArch64::LDSMAXLW,\n    llvm::AArch64::LDSMAXW,\n    llvm::AArch64::LDSMINALW,\n    llvm::AArch64::LDSMINAW,\n    llvm::AArch64::LDSMINLW,\n    llvm::AArch64::LDSMINW,\n    llvm::AArch64::LDUMAXALW,\n    llvm::AArch64::LDUMAXAW,\n    llvm::AArch64::LDUMAXLW,\n    llvm::AArch64::LDUMAXW,\n    llvm::AArch64::LDUMINALW,\n    llvm::AArch64::LDUMINAW,\n    llvm::AArch64::LDUMINLW,\n    llvm::AArch64::LDUMINW,\n    llvm::AArch64::ST1i32,\n    llvm::AArch64::ST1i32_POST,\n    llvm::AArch64::STLLRW,\n    llvm::AArch64::STLRW,\n    llvm::AArch64::STLRWpre,\n    llvm::AArch64::STLURWi,\n    llvm::AArch64::STLURsi,\n    llvm::AArch64::STLXRW,\n    llvm::AArch64::STRSpost,\n    llvm::AArch64::STRSpre,\n    llvm::AArch64::STRSroW,\n    llvm::AArch64::STRSroX,\n    llvm::AArch64::STRSui,\n    llvm::AArch64::STRWpost,\n    llvm::AArch64::STRWpre,\n    llvm::AArch64::STRWroW,\n    llvm::AArch64::STRWroX,\n    llvm::AArch64::STRWui,\n    llvm::AArch64::STTRWi,\n    llvm::AArch64::STURSi,\n    llvm::AArch64::STURWi,\n    llvm::AArch64::STXRW,\n    llvm::AArch64::SWPALW,\n    llvm::AArch64::SWPAW,\n    llvm::AArch64::SWPLW,\n    llvm::AArch64::SWPW,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_32_P1_SIZE = sizeof(WRITE_32_P1) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_32_P2[] = {\n    // clang-format off\n    llvm::AArch64::CASPALW,\n    llvm::AArch64::CASPAW,\n    llvm::AArch64::CASPLW,\n    llvm::AArch64::CASPW,\n    llvm::AArch64::ST2i32,\n    llvm::AArch64::ST2i32_POST,\n    llvm::AArch64::STILPW,\n    llvm::AArch64::STILPWpre,\n    llvm::AArch64::STLXPW,\n    llvm::AArch64::STNPSi,\n    llvm::AArch64::STNPWi,\n    llvm::AArch64::STPSi,\n    llvm::AArch64::STPSpost,\n    llvm::AArch64::STPSpre,\n    llvm::AArch64::STPWi,\n    llvm::AArch64::STPWpost,\n    llvm::AArch64::STPWpre,\n    llvm::AArch64::STXPW,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_32_P2_SIZE = sizeof(WRITE_32_P2) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_32_P3[] = {\n    // clang-format off\n    llvm::AArch64::ST3i32,\n    llvm::AArch64::ST3i32_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_32_P3_SIZE = sizeof(WRITE_32_P3) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_32_P4[] = {\n    // clang-format off\n    llvm::AArch64::ST4i32,\n    llvm::AArch64::ST4i32_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_32_P4_SIZE = sizeof(WRITE_32_P4) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64_P1[] = {\n    // clang-format off\n    llvm::AArch64::CASALX,\n    llvm::AArch64::CASAX,\n    llvm::AArch64::CASLX,\n    llvm::AArch64::CASX,\n    llvm::AArch64::GCSSTR,\n    llvm::AArch64::GCSSTTR,\n    llvm::AArch64::LDADDALX,\n    llvm::AArch64::LDADDAX,\n    llvm::AArch64::LDADDLX,\n    llvm::AArch64::LDADDX,\n    llvm::AArch64::LDCLRALX,\n    llvm::AArch64::LDCLRAX,\n    llvm::AArch64::LDCLRLX,\n    llvm::AArch64::LDCLRX,\n    llvm::AArch64::LDEORALX,\n    llvm::AArch64::LDEORAX,\n    llvm::AArch64::LDEORLX,\n    llvm::AArch64::LDEORX,\n    llvm::AArch64::LDSETALX,\n    llvm::AArch64::LDSETAX,\n    llvm::AArch64::LDSETLX,\n    llvm::AArch64::LDSETX,\n    llvm::AArch64::LDSMAXALX,\n    llvm::AArch64::LDSMAXAX,\n    llvm::AArch64::LDSMAXLX,\n    llvm::AArch64::LDSMAXX,\n    llvm::AArch64::LDSMINALX,\n    llvm::AArch64::LDSMINAX,\n    llvm::AArch64::LDSMINLX,\n    llvm::AArch64::LDSMINX,\n    llvm::AArch64::LDUMAXALX,\n    llvm::AArch64::LDUMAXAX,\n    llvm::AArch64::LDUMAXLX,\n    llvm::AArch64::LDUMAXX,\n    llvm::AArch64::LDUMINALX,\n    llvm::AArch64::LDUMINAX,\n    llvm::AArch64::LDUMINLX,\n    llvm::AArch64::LDUMINX,\n    llvm::AArch64::RCWCAS,\n    llvm::AArch64::RCWCASA,\n    llvm::AArch64::RCWCASAL,\n    llvm::AArch64::RCWCASL,\n    llvm::AArch64::RCWCLR,\n    llvm::AArch64::RCWCLRA,\n    llvm::AArch64::RCWCLRAL,\n    llvm::AArch64::RCWCLRL,\n    llvm::AArch64::RCWCLRS,\n    llvm::AArch64::RCWCLRSA,\n    llvm::AArch64::RCWCLRSAL,\n    llvm::AArch64::RCWCLRSL,\n    llvm::AArch64::RCWSCAS,\n    llvm::AArch64::RCWSCASA,\n    llvm::AArch64::RCWSCASAL,\n    llvm::AArch64::RCWSCASL,\n    llvm::AArch64::RCWSET,\n    llvm::AArch64::RCWSETA,\n    llvm::AArch64::RCWSETAL,\n    llvm::AArch64::RCWSETL,\n    llvm::AArch64::RCWSETS,\n    llvm::AArch64::RCWSETSA,\n    llvm::AArch64::RCWSETSAL,\n    llvm::AArch64::RCWSETSL,\n    llvm::AArch64::RCWSWP,\n    llvm::AArch64::RCWSWPA,\n    llvm::AArch64::RCWSWPAL,\n    llvm::AArch64::RCWSWPL,\n    llvm::AArch64::RCWSWPS,\n    llvm::AArch64::RCWSWPSA,\n    llvm::AArch64::RCWSWPSAL,\n    llvm::AArch64::RCWSWPSL,\n    llvm::AArch64::ST1Onev1d,\n    llvm::AArch64::ST1Onev1d_POST,\n    llvm::AArch64::ST1Onev2s,\n    llvm::AArch64::ST1Onev2s_POST,\n    llvm::AArch64::ST1Onev4h,\n    llvm::AArch64::ST1Onev4h_POST,\n    llvm::AArch64::ST1Onev8b,\n    llvm::AArch64::ST1Onev8b_POST,\n    llvm::AArch64::ST1i64,\n    llvm::AArch64::ST1i64_POST,\n    llvm::AArch64::STL1,\n    llvm::AArch64::STLLRX,\n    llvm::AArch64::STLRX,\n    llvm::AArch64::STLRXpre,\n    llvm::AArch64::STLURXi,\n    llvm::AArch64::STLURdi,\n    llvm::AArch64::STLXRX,\n    llvm::AArch64::STRDpost,\n    llvm::AArch64::STRDpre,\n    llvm::AArch64::STRDroW,\n    llvm::AArch64::STRDroX,\n    llvm::AArch64::STRDui,\n    llvm::AArch64::STRXpost,\n    llvm::AArch64::STRXpre,\n    llvm::AArch64::STRXroW,\n    llvm::AArch64::STRXroX,\n    llvm::AArch64::STRXui,\n    llvm::AArch64::STTRXi,\n    llvm::AArch64::STURDi,\n    llvm::AArch64::STURXi,\n    llvm::AArch64::STXRX,\n    llvm::AArch64::SWPALX,\n    llvm::AArch64::SWPAX,\n    llvm::AArch64::SWPLX,\n    llvm::AArch64::SWPX,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_P1_SIZE = sizeof(WRITE_64_P1) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64_P2[] = {\n    // clang-format off\n    llvm::AArch64::CASPALX,\n    llvm::AArch64::CASPAX,\n    llvm::AArch64::CASPLX,\n    llvm::AArch64::CASPX,\n    llvm::AArch64::RCWCASP,\n    llvm::AArch64::RCWCASPA,\n    llvm::AArch64::RCWCASPAL,\n    llvm::AArch64::RCWCASPL,\n    llvm::AArch64::RCWCLRP,\n    llvm::AArch64::RCWCLRPA,\n    llvm::AArch64::RCWCLRPAL,\n    llvm::AArch64::RCWCLRPL,\n    llvm::AArch64::RCWCLRSP,\n    llvm::AArch64::RCWCLRSPA,\n    llvm::AArch64::RCWCLRSPAL,\n    llvm::AArch64::RCWCLRSPL,\n    llvm::AArch64::RCWSCASP,\n    llvm::AArch64::RCWSCASPA,\n    llvm::AArch64::RCWSCASPAL,\n    llvm::AArch64::RCWSCASPL,\n    llvm::AArch64::RCWSETP,\n    llvm::AArch64::RCWSETPA,\n    llvm::AArch64::RCWSETPAL,\n    llvm::AArch64::RCWSETPL,\n    llvm::AArch64::RCWSETSP,\n    llvm::AArch64::RCWSETSPA,\n    llvm::AArch64::RCWSETSPAL,\n    llvm::AArch64::RCWSETSPL,\n    llvm::AArch64::RCWSWPP,\n    llvm::AArch64::RCWSWPPA,\n    llvm::AArch64::RCWSWPPAL,\n    llvm::AArch64::RCWSWPPL,\n    llvm::AArch64::RCWSWPSP,\n    llvm::AArch64::RCWSWPSPA,\n    llvm::AArch64::RCWSWPSPAL,\n    llvm::AArch64::RCWSWPSPL,\n    llvm::AArch64::ST1Twov1d,\n    llvm::AArch64::ST1Twov1d_POST,\n    llvm::AArch64::ST1Twov2s,\n    llvm::AArch64::ST1Twov2s_POST,\n    llvm::AArch64::ST1Twov4h,\n    llvm::AArch64::ST1Twov4h_POST,\n    llvm::AArch64::ST1Twov8b,\n    llvm::AArch64::ST1Twov8b_POST,\n    llvm::AArch64::ST2Twov2s,\n    llvm::AArch64::ST2Twov2s_POST,\n    llvm::AArch64::ST2Twov4h,\n    llvm::AArch64::ST2Twov4h_POST,\n    llvm::AArch64::ST2Twov8b,\n    llvm::AArch64::ST2Twov8b_POST,\n    llvm::AArch64::ST2i64,\n    llvm::AArch64::ST2i64_POST,\n    llvm::AArch64::STILPX,\n    llvm::AArch64::STILPXpre,\n    llvm::AArch64::STLXPX,\n    llvm::AArch64::STNPDi,\n    llvm::AArch64::STNPXi,\n    llvm::AArch64::STPDi,\n    llvm::AArch64::STPDpost,\n    llvm::AArch64::STPDpre,\n    llvm::AArch64::STPXi,\n    llvm::AArch64::STPXpost,\n    llvm::AArch64::STPXpre,\n    llvm::AArch64::STXPX,\n    llvm::AArch64::SWPP,\n    llvm::AArch64::SWPPA,\n    llvm::AArch64::SWPPAL,\n    llvm::AArch64::SWPPL,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_P2_SIZE = sizeof(WRITE_64_P2) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64_P3[] = {\n    // clang-format off\n    llvm::AArch64::ST1Threev1d,\n    llvm::AArch64::ST1Threev1d_POST,\n    llvm::AArch64::ST1Threev2s,\n    llvm::AArch64::ST1Threev2s_POST,\n    llvm::AArch64::ST1Threev4h,\n    llvm::AArch64::ST1Threev4h_POST,\n    llvm::AArch64::ST1Threev8b,\n    llvm::AArch64::ST1Threev8b_POST,\n    llvm::AArch64::ST3Threev2s,\n    llvm::AArch64::ST3Threev2s_POST,\n    llvm::AArch64::ST3Threev4h,\n    llvm::AArch64::ST3Threev4h_POST,\n    llvm::AArch64::ST3Threev8b,\n    llvm::AArch64::ST3Threev8b_POST,\n    llvm::AArch64::ST3i64,\n    llvm::AArch64::ST3i64_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_P3_SIZE = sizeof(WRITE_64_P3) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64_P4[] = {\n    // clang-format off\n    llvm::AArch64::ST1Fourv1d,\n    llvm::AArch64::ST1Fourv1d_POST,\n    llvm::AArch64::ST1Fourv2s,\n    llvm::AArch64::ST1Fourv2s_POST,\n    llvm::AArch64::ST1Fourv4h,\n    llvm::AArch64::ST1Fourv4h_POST,\n    llvm::AArch64::ST1Fourv8b,\n    llvm::AArch64::ST1Fourv8b_POST,\n    llvm::AArch64::ST4Fourv2s,\n    llvm::AArch64::ST4Fourv2s_POST,\n    llvm::AArch64::ST4Fourv4h,\n    llvm::AArch64::ST4Fourv4h_POST,\n    llvm::AArch64::ST4Fourv8b,\n    llvm::AArch64::ST4Fourv8b_POST,\n    llvm::AArch64::ST4i64,\n    llvm::AArch64::ST4i64_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_P4_SIZE = sizeof(WRITE_64_P4) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64_P8[] = {\n    // clang-format off\n    llvm::AArch64::ST64B,\n    llvm::AArch64::ST64BV,\n    llvm::AArch64::ST64BV0,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_P8_SIZE = sizeof(WRITE_64_P8) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_128_P1[] = {\n    // clang-format off\n    llvm::AArch64::LDCLRP,\n    llvm::AArch64::LDCLRPA,\n    llvm::AArch64::LDCLRPAL,\n    llvm::AArch64::LDCLRPL,\n    llvm::AArch64::LDSETP,\n    llvm::AArch64::LDSETPA,\n    llvm::AArch64::LDSETPAL,\n    llvm::AArch64::LDSETPL,\n    llvm::AArch64::ST1Onev16b,\n    llvm::AArch64::ST1Onev16b_POST,\n    llvm::AArch64::ST1Onev2d,\n    llvm::AArch64::ST1Onev2d_POST,\n    llvm::AArch64::ST1Onev4s,\n    llvm::AArch64::ST1Onev4s_POST,\n    llvm::AArch64::ST1Onev8h,\n    llvm::AArch64::ST1Onev8h_POST,\n    llvm::AArch64::STLURqi,\n    llvm::AArch64::STRQpost,\n    llvm::AArch64::STRQpre,\n    llvm::AArch64::STRQroW,\n    llvm::AArch64::STRQroX,\n    llvm::AArch64::STRQui,\n    llvm::AArch64::STURQi,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_128_P1_SIZE = sizeof(WRITE_128_P1) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_128_P2[] = {\n    // clang-format off\n    llvm::AArch64::ST1Twov16b,\n    llvm::AArch64::ST1Twov16b_POST,\n    llvm::AArch64::ST1Twov2d,\n    llvm::AArch64::ST1Twov2d_POST,\n    llvm::AArch64::ST1Twov4s,\n    llvm::AArch64::ST1Twov4s_POST,\n    llvm::AArch64::ST1Twov8h,\n    llvm::AArch64::ST1Twov8h_POST,\n    llvm::AArch64::ST2Twov16b,\n    llvm::AArch64::ST2Twov16b_POST,\n    llvm::AArch64::ST2Twov2d,\n    llvm::AArch64::ST2Twov2d_POST,\n    llvm::AArch64::ST2Twov4s,\n    llvm::AArch64::ST2Twov4s_POST,\n    llvm::AArch64::ST2Twov8h,\n    llvm::AArch64::ST2Twov8h_POST,\n    llvm::AArch64::STNPQi,\n    llvm::AArch64::STPQi,\n    llvm::AArch64::STPQpost,\n    llvm::AArch64::STPQpre,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_128_P2_SIZE = sizeof(WRITE_128_P2) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_128_P3[] = {\n    // clang-format off\n    llvm::AArch64::ST1Threev16b,\n    llvm::AArch64::ST1Threev16b_POST,\n    llvm::AArch64::ST1Threev2d,\n    llvm::AArch64::ST1Threev2d_POST,\n    llvm::AArch64::ST1Threev4s,\n    llvm::AArch64::ST1Threev4s_POST,\n    llvm::AArch64::ST1Threev8h,\n    llvm::AArch64::ST1Threev8h_POST,\n    llvm::AArch64::ST3Threev16b,\n    llvm::AArch64::ST3Threev16b_POST,\n    llvm::AArch64::ST3Threev2d,\n    llvm::AArch64::ST3Threev2d_POST,\n    llvm::AArch64::ST3Threev4s,\n    llvm::AArch64::ST3Threev4s_POST,\n    llvm::AArch64::ST3Threev8h,\n    llvm::AArch64::ST3Threev8h_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_128_P3_SIZE = sizeof(WRITE_128_P3) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_128_P4[] = {\n    // clang-format off\n    llvm::AArch64::ST1Fourv16b,\n    llvm::AArch64::ST1Fourv16b_POST,\n    llvm::AArch64::ST1Fourv2d,\n    llvm::AArch64::ST1Fourv2d_POST,\n    llvm::AArch64::ST1Fourv4s,\n    llvm::AArch64::ST1Fourv4s_POST,\n    llvm::AArch64::ST1Fourv8h,\n    llvm::AArch64::ST1Fourv8h_POST,\n    llvm::AArch64::ST4Fourv16b,\n    llvm::AArch64::ST4Fourv16b_POST,\n    llvm::AArch64::ST4Fourv2d,\n    llvm::AArch64::ST4Fourv2d_POST,\n    llvm::AArch64::ST4Fourv4s,\n    llvm::AArch64::ST4Fourv4s_POST,\n    llvm::AArch64::ST4Fourv8h,\n    llvm::AArch64::ST4Fourv8h_POST,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_128_P4_SIZE = sizeof(WRITE_128_P4) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_DYN[] = {\n    // clang-format off\n    llvm::AArch64::CPYE,\n    llvm::AArch64::CPYEN,\n    llvm::AArch64::CPYERN,\n    llvm::AArch64::CPYERT,\n    llvm::AArch64::CPYERTN,\n    llvm::AArch64::CPYERTRN,\n    llvm::AArch64::CPYERTWN,\n    llvm::AArch64::CPYET,\n    llvm::AArch64::CPYETN,\n    llvm::AArch64::CPYETRN,\n    llvm::AArch64::CPYETWN,\n    llvm::AArch64::CPYEWN,\n    llvm::AArch64::CPYEWT,\n    llvm::AArch64::CPYEWTN,\n    llvm::AArch64::CPYEWTRN,\n    llvm::AArch64::CPYEWTWN,\n    llvm::AArch64::CPYFE,\n    llvm::AArch64::CPYFEN,\n    llvm::AArch64::CPYFERN,\n    llvm::AArch64::CPYFERT,\n    llvm::AArch64::CPYFERTN,\n    llvm::AArch64::CPYFERTRN,\n    llvm::AArch64::CPYFERTWN,\n    llvm::AArch64::CPYFET,\n    llvm::AArch64::CPYFETN,\n    llvm::AArch64::CPYFETRN,\n    llvm::AArch64::CPYFETWN,\n    llvm::AArch64::CPYFEWN,\n    llvm::AArch64::CPYFEWT,\n    llvm::AArch64::CPYFEWTN,\n    llvm::AArch64::CPYFEWTRN,\n    llvm::AArch64::CPYFEWTWN,\n    llvm::AArch64::CPYFM,\n    llvm::AArch64::CPYFMN,\n    llvm::AArch64::CPYFMRN,\n    llvm::AArch64::CPYFMRT,\n    llvm::AArch64::CPYFMRTN,\n    llvm::AArch64::CPYFMRTRN,\n    llvm::AArch64::CPYFMRTWN,\n    llvm::AArch64::CPYFMT,\n    llvm::AArch64::CPYFMTN,\n    llvm::AArch64::CPYFMTRN,\n    llvm::AArch64::CPYFMTWN,\n    llvm::AArch64::CPYFMWN,\n    llvm::AArch64::CPYFMWT,\n    llvm::AArch64::CPYFMWTN,\n    llvm::AArch64::CPYFMWTRN,\n    llvm::AArch64::CPYFMWTWN,\n    llvm::AArch64::CPYFP,\n    llvm::AArch64::CPYFPN,\n    llvm::AArch64::CPYFPRN,\n    llvm::AArch64::CPYFPRT,\n    llvm::AArch64::CPYFPRTN,\n    llvm::AArch64::CPYFPRTRN,\n    llvm::AArch64::CPYFPRTWN,\n    llvm::AArch64::CPYFPT,\n    llvm::AArch64::CPYFPTN,\n    llvm::AArch64::CPYFPTRN,\n    llvm::AArch64::CPYFPTWN,\n    llvm::AArch64::CPYFPWN,\n    llvm::AArch64::CPYFPWT,\n    llvm::AArch64::CPYFPWTN,\n    llvm::AArch64::CPYFPWTRN,\n    llvm::AArch64::CPYFPWTWN,\n    llvm::AArch64::CPYM,\n    llvm::AArch64::CPYMN,\n    llvm::AArch64::CPYMRN,\n    llvm::AArch64::CPYMRT,\n    llvm::AArch64::CPYMRTN,\n    llvm::AArch64::CPYMRTRN,\n    llvm::AArch64::CPYMRTWN,\n    llvm::AArch64::CPYMT,\n    llvm::AArch64::CPYMTN,\n    llvm::AArch64::CPYMTRN,\n    llvm::AArch64::CPYMTWN,\n    llvm::AArch64::CPYMWN,\n    llvm::AArch64::CPYMWT,\n    llvm::AArch64::CPYMWTN,\n    llvm::AArch64::CPYMWTRN,\n    llvm::AArch64::CPYMWTWN,\n    llvm::AArch64::CPYP,\n    llvm::AArch64::CPYPN,\n    llvm::AArch64::CPYPRN,\n    llvm::AArch64::CPYPRT,\n    llvm::AArch64::CPYPRTN,\n    llvm::AArch64::CPYPRTRN,\n    llvm::AArch64::CPYPRTWN,\n    llvm::AArch64::CPYPT,\n    llvm::AArch64::CPYPTN,\n    llvm::AArch64::CPYPTRN,\n    llvm::AArch64::CPYPTWN,\n    llvm::AArch64::CPYPWN,\n    llvm::AArch64::CPYPWT,\n    llvm::AArch64::CPYPWTN,\n    llvm::AArch64::CPYPWTRN,\n    llvm::AArch64::CPYPWTWN,\n    llvm::AArch64::MOPSSETGE,\n    llvm::AArch64::MOPSSETGEN,\n    llvm::AArch64::MOPSSETGET,\n    llvm::AArch64::MOPSSETGETN,\n    llvm::AArch64::SETE,\n    llvm::AArch64::SETEN,\n    llvm::AArch64::SETET,\n    llvm::AArch64::SETETN,\n    llvm::AArch64::SETGM,\n    llvm::AArch64::SETGMN,\n    llvm::AArch64::SETGMT,\n    llvm::AArch64::SETGMTN,\n    llvm::AArch64::SETGP,\n    llvm::AArch64::SETGPN,\n    llvm::AArch64::SETGPT,\n    llvm::AArch64::SETGPTN,\n    llvm::AArch64::SETM,\n    llvm::AArch64::SETMN,\n    llvm::AArch64::SETMT,\n    llvm::AArch64::SETMTN,\n    llvm::AArch64::SETP,\n    llvm::AArch64::SETPN,\n    llvm::AArch64::SETPT,\n    llvm::AArch64::SETPTN,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_DYN_SIZE = sizeof(WRITE_DYN) / sizeof(unsigned);\n\n/* Highest 16 bits are the write access, lowest 16 bits are the read access.\n *\n * ------------------------------------------------------------\n * | 0x1f                  WRITE ACCESS                  0x10 |\n * ------------------------------------------------------------\n * | 1 bit dyn | 4 bits unused | 11 bits unsigned access size |\n * ------------------------------------------------------------\n *\n * ------------------------------------------------------------\n * | 0xf                   READ ACCESS                    0x0 |\n * ------------------------------------------------------------\n * | 1 bit dyn | 4 bits unused | 11 bits unsigned access size |\n * ------------------------------------------------------------\n */\n\nconstexpr uint32_t READ_DYN_FLAGS = 15;\nconstexpr uint32_t WRITE_POSITION = 16;\nconstexpr uint32_t WRITE_DYN_FLAGS = WRITE_POSITION + READ_DYN_FLAGS;\nconstexpr uint32_t READ(uint32_t s, uint32_t p) { return (s * p) & 0x3ff; }\nconstexpr uint32_t WRITE(uint32_t s, uint32_t p) {\n  return ((s * p) & 0x3ff) << WRITE_POSITION;\n}\nconstexpr uint32_t GET_READ_SIZE(uint32_t v) { return v & 0x3ff; }\nconstexpr uint32_t GET_WRITE_SIZE(uint32_t v) {\n  return (v >> WRITE_POSITION) & 0x3ff;\n}\nconstexpr uint32_t IS_READ_DYN(uint32_t v) { return (v >> READ_DYN_FLAGS) & 1; }\nconstexpr uint32_t IS_WRITE_DYN(uint32_t v) {\n  return (v >> WRITE_DYN_FLAGS) & 1;\n}\n\nstruct MemAccessArray {\n  uint32_t arr[llvm::AArch64::INSTRUCTION_LIST_END] = {0};\n\n  constexpr inline void _initMemAccessRead(const unsigned buff[],\n                                           const size_t buffSize, uint32_t len,\n                                           uint32_t pack) {\n    for (size_t i = 0; i < buffSize; i++) {\n      arr[buff[i]] |= READ(len, pack);\n    }\n  }\n\n  constexpr inline void _initMemAccessWrite(const unsigned buff[],\n                                            const size_t buffSize, uint32_t len,\n                                            uint32_t pack) {\n    for (size_t i = 0; i < buffSize; i++) {\n      arr[buff[i]] |= WRITE(len, pack);\n    }\n  }\n\n  constexpr inline void _initMemAccessFlags(const unsigned buff[],\n                                            const size_t buffSize,\n                                            uint32_t flags) {\n    for (size_t i = 0; i < buffSize; i++) {\n      arr[buff[i]] |= flags;\n    }\n  }\n\n  constexpr MemAccessArray() {\n    // read\n    _initMemAccessRead(READ_8_P1, READ_8_P1_SIZE, 1, 1);\n    _initMemAccessRead(READ_8_P2, READ_8_P2_SIZE, 1, 2);\n    _initMemAccessRead(READ_8_P3, READ_8_P3_SIZE, 1, 3);\n    _initMemAccessRead(READ_8_P4, READ_8_P4_SIZE, 1, 4);\n    _initMemAccessRead(READ_16_P1, READ_16_P1_SIZE, 2, 1);\n    _initMemAccessRead(READ_16_P2, READ_16_P2_SIZE, 2, 2);\n    _initMemAccessRead(READ_16_P3, READ_16_P3_SIZE, 2, 3);\n    _initMemAccessRead(READ_16_P4, READ_16_P4_SIZE, 2, 4);\n    _initMemAccessRead(READ_32_P1, READ_32_P1_SIZE, 4, 1);\n    _initMemAccessRead(READ_32_P2, READ_32_P2_SIZE, 4, 2);\n    _initMemAccessRead(READ_32_P3, READ_32_P3_SIZE, 4, 3);\n    _initMemAccessRead(READ_32_P4, READ_32_P4_SIZE, 4, 4);\n    _initMemAccessRead(READ_64_P1, READ_64_P1_SIZE, 8, 1);\n    _initMemAccessRead(READ_64_P2, READ_64_P2_SIZE, 8, 2);\n    _initMemAccessRead(READ_64_P3, READ_64_P3_SIZE, 8, 3);\n    _initMemAccessRead(READ_64_P4, READ_64_P4_SIZE, 8, 4);\n    _initMemAccessRead(READ_64_P8, READ_64_P8_SIZE, 8, 8);\n    _initMemAccessRead(READ_128_P1, READ_128_P1_SIZE, 16, 1);\n    _initMemAccessRead(READ_128_P2, READ_128_P2_SIZE, 16, 2);\n    _initMemAccessRead(READ_128_P3, READ_128_P3_SIZE, 16, 3);\n    _initMemAccessRead(READ_128_P4, READ_128_P4_SIZE, 16, 4);\n    // read dyn\n    _initMemAccessFlags(READ_DYN, READ_DYN_SIZE, (1 << READ_DYN_FLAGS));\n    // write\n    _initMemAccessWrite(WRITE_8_P1, WRITE_8_P1_SIZE, 1, 1);\n    _initMemAccessWrite(WRITE_8_P2, WRITE_8_P2_SIZE, 1, 2);\n    _initMemAccessWrite(WRITE_8_P3, WRITE_8_P3_SIZE, 1, 3);\n    _initMemAccessWrite(WRITE_8_P4, WRITE_8_P4_SIZE, 1, 4);\n    _initMemAccessWrite(WRITE_16_P1, WRITE_16_P1_SIZE, 2, 1);\n    _initMemAccessWrite(WRITE_16_P2, WRITE_16_P2_SIZE, 2, 2);\n    _initMemAccessWrite(WRITE_16_P3, WRITE_16_P3_SIZE, 2, 3);\n    _initMemAccessWrite(WRITE_16_P4, WRITE_16_P4_SIZE, 2, 4);\n    _initMemAccessWrite(WRITE_32_P1, WRITE_32_P1_SIZE, 4, 1);\n    _initMemAccessWrite(WRITE_32_P2, WRITE_32_P2_SIZE, 4, 2);\n    _initMemAccessWrite(WRITE_32_P3, WRITE_32_P3_SIZE, 4, 3);\n    _initMemAccessWrite(WRITE_32_P4, WRITE_32_P4_SIZE, 4, 4);\n    _initMemAccessWrite(WRITE_64_P1, WRITE_64_P1_SIZE, 8, 1);\n    _initMemAccessWrite(WRITE_64_P2, WRITE_64_P2_SIZE, 8, 2);\n    _initMemAccessWrite(WRITE_64_P3, WRITE_64_P3_SIZE, 8, 3);\n    _initMemAccessWrite(WRITE_64_P4, WRITE_64_P4_SIZE, 8, 4);\n    _initMemAccessWrite(WRITE_64_P8, WRITE_64_P8_SIZE, 8, 8);\n    _initMemAccessWrite(WRITE_128_P1, WRITE_128_P1_SIZE, 16, 1);\n    _initMemAccessWrite(WRITE_128_P2, WRITE_128_P2_SIZE, 16, 2);\n    _initMemAccessWrite(WRITE_128_P3, WRITE_128_P3_SIZE, 16, 3);\n    _initMemAccessWrite(WRITE_128_P4, WRITE_128_P4_SIZE, 16, 4);\n    // write dyn\n    _initMemAccessFlags(WRITE_DYN, WRITE_DYN_SIZE, (1 << WRITE_DYN_FLAGS));\n  }\n\n#if CHECK_INSTINFO_TABLE\n  void check_table(const unsigned buff[], const size_t buffSize, uint32_t value,\n                   uint32_t mask) const {\n    for (size_t i = 0; i < buffSize; i++) {\n      unsigned instID = buff[i];\n      if ((arr[instID] & mask) != value) {\n        fprintf(stderr,\n                \"[MemAccessArray::check_table], opcode %d, mask %x, expected \"\n                \"%x, found %x\\n\",\n                instID, mask, value, (arr[instID] & mask));\n        abort();\n      }\n    }\n  }\n\n  int check() const {\n    // read\n    check_table(READ_8_P1, READ_8_P1_SIZE, READ(1, 1), 0x7ff);\n    check_table(READ_8_P2, READ_8_P2_SIZE, READ(1, 2), 0x7ff);\n    check_table(READ_8_P3, READ_8_P3_SIZE, READ(1, 3), 0x7ff);\n    check_table(READ_8_P4, READ_8_P4_SIZE, READ(1, 4), 0x7ff);\n    check_table(READ_16_P1, READ_16_P1_SIZE, READ(2, 1), 0x7ff);\n    check_table(READ_16_P2, READ_16_P2_SIZE, READ(2, 2), 0x7ff);\n    check_table(READ_16_P3, READ_16_P3_SIZE, READ(2, 3), 0x7ff);\n    check_table(READ_16_P4, READ_16_P4_SIZE, READ(2, 4), 0x7ff);\n    check_table(READ_32_P1, READ_32_P1_SIZE, READ(4, 1), 0x7ff);\n    check_table(READ_32_P2, READ_32_P2_SIZE, READ(4, 2), 0x7ff);\n    check_table(READ_32_P3, READ_32_P3_SIZE, READ(4, 3), 0x7ff);\n    check_table(READ_32_P4, READ_32_P4_SIZE, READ(4, 4), 0x7ff);\n    check_table(READ_64_P1, READ_64_P1_SIZE, READ(8, 1), 0x7ff);\n    check_table(READ_64_P2, READ_64_P2_SIZE, READ(8, 2), 0x7ff);\n    check_table(READ_64_P3, READ_64_P3_SIZE, READ(8, 3), 0x7ff);\n    check_table(READ_64_P4, READ_64_P4_SIZE, READ(8, 4), 0x7ff);\n    check_table(READ_64_P8, READ_64_P8_SIZE, READ(8, 8), 0x7ff);\n    check_table(READ_128_P1, READ_128_P1_SIZE, READ(16, 1), 0x7ff);\n    check_table(READ_128_P2, READ_128_P2_SIZE, READ(16, 2), 0x7ff);\n    check_table(READ_128_P3, READ_128_P3_SIZE, READ(16, 3), 0x7ff);\n    check_table(READ_128_P4, READ_128_P4_SIZE, READ(16, 4), 0x7ff);\n    check_table(READ_DYN, READ_DYN_SIZE, (1 << READ_DYN_FLAGS),\n                (1 << READ_DYN_FLAGS));\n    // write\n    check_table(WRITE_8_P1, WRITE_8_P1_SIZE, WRITE(1, 1),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_8_P2, WRITE_8_P2_SIZE, WRITE(1, 2),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_8_P3, WRITE_8_P3_SIZE, WRITE(1, 3),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_8_P4, WRITE_8_P4_SIZE, WRITE(1, 4),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_16_P1, WRITE_16_P1_SIZE, WRITE(2, 1),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_16_P2, WRITE_16_P2_SIZE, WRITE(2, 2),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_16_P3, WRITE_16_P3_SIZE, WRITE(2, 3),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_16_P4, WRITE_16_P4_SIZE, WRITE(2, 4),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_32_P1, WRITE_32_P1_SIZE, WRITE(4, 1),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_32_P2, WRITE_32_P2_SIZE, WRITE(4, 2),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_32_P3, WRITE_32_P3_SIZE, WRITE(4, 3),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_32_P4, WRITE_32_P4_SIZE, WRITE(4, 4),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_64_P1, WRITE_64_P1_SIZE, WRITE(8, 1),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_64_P2, WRITE_64_P2_SIZE, WRITE(8, 2),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_64_P3, WRITE_64_P3_SIZE, WRITE(8, 3),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_64_P4, WRITE_64_P4_SIZE, WRITE(8, 4),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_64_P8, WRITE_64_P8_SIZE, WRITE(8, 8),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_128_P1, WRITE_128_P1_SIZE, WRITE(16, 1),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_128_P2, WRITE_128_P2_SIZE, WRITE(16, 2),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_128_P3, WRITE_128_P3_SIZE, WRITE(16, 3),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_128_P4, WRITE_128_P4_SIZE, WRITE(16, 4),\n                0x7ff << WRITE_POSITION);\n    check_table(WRITE_DYN, WRITE_DYN_SIZE, (1 << WRITE_DYN_FLAGS),\n                (1 << WRITE_DYN_FLAGS));\n    return 0;\n  }\n#endif\n\n  inline uint32_t get(size_t op) const {\n    if (op < llvm::AArch64::INSTRUCTION_LIST_END) {\n      return arr[op];\n    }\n\n    QBDI_ERROR(\"No opcode {}\", op);\n    return 0;\n  }\n};\nconstexpr MemAccessArray memAccessCache;\n\n#if CHECK_INSTINFO_TABLE\nint __check_debug = memAccessCache.check();\n#endif\n\n} // anonymous namespace\n\nunsigned getReadSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  return GET_READ_SIZE(memAccessCache.get(inst.getOpcode()));\n}\n\nunsigned getWriteSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  return GET_WRITE_SIZE(memAccessCache.get(inst.getOpcode()));\n}\n\nunsigned getInstSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  return 4;\n}\n\nsword getFixedOperandValue(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                           unsigned index, int64_t value) {\n  return static_cast<rword>(value);\n}\n\nunsigned getImmediateSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  // TODO check llvm\n  return 2;\n}\n\nbool unsupportedRead(const llvm::MCInst &inst) {\n  return IS_READ_DYN(memAccessCache.get(inst.getOpcode()));\n}\n\nbool unsupportedWrite(const llvm::MCInst &inst) {\n  return IS_WRITE_DYN(memAccessCache.get(inst.getOpcode()));\n}\n\nbool variadicOpsIsWrite(const llvm::MCInst &inst) { return false; }\n\nbool isMOPSPrologue(const llvm::MCInst &inst) {\n\n  switch (inst.getOpcode()) {\n    default:\n      return false;\n    case llvm::AArch64::CPYFP:\n    case llvm::AArch64::CPYFPN:\n    case llvm::AArch64::CPYFPRN:\n    case llvm::AArch64::CPYFPRT:\n    case llvm::AArch64::CPYFPRTN:\n    case llvm::AArch64::CPYFPRTRN:\n    case llvm::AArch64::CPYFPRTWN:\n    case llvm::AArch64::CPYFPT:\n    case llvm::AArch64::CPYFPTN:\n    case llvm::AArch64::CPYFPTRN:\n    case llvm::AArch64::CPYFPTWN:\n    case llvm::AArch64::CPYFPWN:\n    case llvm::AArch64::CPYFPWT:\n    case llvm::AArch64::CPYFPWTN:\n    case llvm::AArch64::CPYFPWTRN:\n    case llvm::AArch64::CPYFPWTWN:\n    case llvm::AArch64::CPYP:\n    case llvm::AArch64::CPYPN:\n    case llvm::AArch64::CPYPRN:\n    case llvm::AArch64::CPYPRT:\n    case llvm::AArch64::CPYPRTN:\n    case llvm::AArch64::CPYPRTRN:\n    case llvm::AArch64::CPYPRTWN:\n    case llvm::AArch64::CPYPT:\n    case llvm::AArch64::CPYPTN:\n    case llvm::AArch64::CPYPTRN:\n    case llvm::AArch64::CPYPTWN:\n    case llvm::AArch64::CPYPWN:\n    case llvm::AArch64::CPYPWT:\n    case llvm::AArch64::CPYPWTN:\n    case llvm::AArch64::CPYPWTRN:\n    case llvm::AArch64::CPYPWTWN:\n    case llvm::AArch64::SETGP:\n    case llvm::AArch64::SETGPN:\n    case llvm::AArch64::SETGPT:\n    case llvm::AArch64::SETGPTN:\n    case llvm::AArch64::SETP:\n    case llvm::AArch64::SETPN:\n    case llvm::AArch64::SETPT:\n    case llvm::AArch64::SETPTN:\n      return true;\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/InstInfo_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTINFO_AARCH64_H\n#define INSTINFO_AARCH64_H\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\n\nunsigned getReadPack(const llvm::MCInst &inst);\nunsigned getWritePack(const llvm::MCInst &inst);\nbool isMOPSPrologue(const llvm::MCInst &inst);\n\n}; // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/InstMetadata_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTMETADATA_AARCH64_H\n#define INSTMETADATA_AARCH64_H\n\nnamespace QBDI {\n\nstruct InstMetadataArch {\n  // nothing for AARCH64\n};\n\n} // namespace QBDI\n\n#endif // INSTMETADATA_AARCH64_H\n"
  },
  {
    "path": "src/Patch/AARCH64/InstrRules_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"ExecBlock/Context.h\"\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/PatchGenerator_AARCH64.h\"\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Patch/InstrRules.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\n/* Genreate a series of RelocatableInst which when appended to an\n * instrumentation code trigger a break to host. It receive in argument a\n * temporary reg which will be used for computations then finally restored.\n */\nRelocatableInst::UniquePtrVec getBreakToHost(Reg temp, const Patch &patch,\n                                             bool restore) {\n  RelocatableInst::UniquePtrVec breakToHost;\n\n  size_t patchSize = 12;\n  if (restore) {\n    patchSize += 4;\n  }\n\n  // compute address after JmpEpilogue\n  breakToHost.push_back(Adr(temp, patchSize));\n  // set address in hostState.selector\n  append(breakToHost,\n         SaveReg(temp, Offset(offsetof(Context, hostState.selector)))\n             .genReloc(*patch.llvmcpu));\n  if (restore) {\n    // Restore the temporary register\n    append(breakToHost, LoadReg(temp, Offset(temp)).genReloc(*patch.llvmcpu));\n  }\n\n  append(breakToHost, JmpEpilogue().genReloc(*patch.llvmcpu));\n\n  // add target when callback return CONTINUE\n  append(breakToHost, TargetPrologue().genReloc(patch));\n\n  return breakToHost;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/Layer2_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"AArch64InstrInfo.h\"\n#include \"MCTargetDesc/AArch64AddressingModes.h\"\n#include \"Utils/AArch64BaseInfo.h\"\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nunsigned format_as(ShiftExtendType type) { return fmt::underlying(type); }\n\nllvm::MCInst st1_post_inc(RegLLVM regs, RegLLVM base) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::ST1Fourv2d_POST);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(regs.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::AArch64::XZR));\n  return inst;\n}\n\nllvm::MCInst ld1_post_inc(RegLLVM regs, RegLLVM base) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LD1Fourv2d_POST);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(regs.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::AArch64::XZR));\n  return inst;\n}\n\nllvm::MCInst addr(RegLLVM dst, RegLLVM src) { return addr(dst, dst, src); }\n\nllvm::MCInst addr(RegLLVM dst, RegLLVM src1, RegLLVM src2) {\n  QBDI_REQUIRE_ABORT(src2 != llvm::AArch64::SP,\n                     \"SP cannot be the second operand\");\n  llvm::MCInst inst;\n  if (dst != llvm::AArch64::SP and src1 != llvm::AArch64::SP) {\n    inst.setOpcode(llvm::AArch64::ADDXrs);\n    inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src1.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n    inst.addOperand(llvm::MCOperand::createImm(0));\n  } else {\n    inst.setOpcode(llvm::AArch64::ADDXrx64);\n    inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src1.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n    inst.addOperand(llvm::MCOperand::createImm(llvm::AArch64_AM::UXTX << 3));\n  }\n  return inst;\n}\n\nllvm::MCInst addr(RegLLVM dst, RegLLVM src1, RegLLVM src2, ShiftExtendType type,\n                  unsigned int shift) {\n\n  QBDI_REQUIRE_ABORT(shift <= 4, \"Unsupported shift {}\", shift);\n  unsigned immValue;\n  switch (type) {\n    case UXTB:\n      immValue = getArithExtendImm(llvm::AArch64_AM::UXTB, shift);\n      break;\n    case UXTH:\n      immValue = getArithExtendImm(llvm::AArch64_AM::UXTH, shift);\n      break;\n    case UXTW:\n      immValue = getArithExtendImm(llvm::AArch64_AM::UXTW, shift);\n      break;\n    case UXTX:\n      immValue = getArithExtendImm(llvm::AArch64_AM::UXTX, shift);\n      break;\n    case SXTB:\n      immValue = getArithExtendImm(llvm::AArch64_AM::SXTB, shift);\n      break;\n    case SXTH:\n      immValue = getArithExtendImm(llvm::AArch64_AM::SXTH, shift);\n      break;\n    case SXTW:\n      immValue = getArithExtendImm(llvm::AArch64_AM::SXTW, shift);\n      break;\n    case SXTX:\n      immValue = getArithExtendImm(llvm::AArch64_AM::SXTX, shift);\n      break;\n    default:\n      QBDI_ABORT(\"Unexpected type %d\", type);\n  }\n\n  llvm::MCInst inst;\n  RegLLVM extReg;\n  if (type == UXTX || type == SXTX) {\n    extReg = llvm::getXRegFromWReg(src2.getValue());\n    inst.setOpcode(llvm::AArch64::ADDXrx64);\n  } else {\n    extReg = llvm::getWRegFromXReg(src2.getValue());\n    inst.setOpcode(llvm::AArch64::ADDXrx);\n  }\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src1.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(extReg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(immValue));\n  return inst;\n}\n\nllvm::MCInst addri(RegLLVM dst, RegLLVM src, rword offset) {\n  int shift = 0;\n  if (offset != 0 && (offset % 4096) == 0) {\n    shift = 12;\n    offset = offset >> 12;\n    QBDI_REQUIRE_ABORT(offset < 4096, \"Must be a lower than 2**24 : {}\",\n                       offset);\n  } else {\n    QBDI_REQUIRE_ABORT(offset < 4096, \"Must be a lower than 4096 : {}\", offset);\n  }\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::ADDXri);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(shift));\n  return inst;\n}\n\nllvm::MCInst subr(RegLLVM dst, RegLLVM src) { return subr(dst, dst, src); }\n\nllvm::MCInst subr(RegLLVM dst, RegLLVM src1, RegLLVM src2) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(src2 != llvm::AArch64::SP,\n                     \"SP cannot be the second operand\");\n  if (dst != llvm::AArch64::SP and src1 != llvm::AArch64::SP) {\n\n    inst.setOpcode(llvm::AArch64::SUBXrs);\n    inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src1.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n    inst.addOperand(llvm::MCOperand::createImm(0));\n  } else {\n\n    inst.setOpcode(llvm::AArch64::SUBXrx64);\n    inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src1.getValue()));\n    inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n    inst.addOperand(llvm::MCOperand::createImm(llvm::AArch64_AM::UXTX << 3));\n  }\n  return inst;\n}\n\nllvm::MCInst subri(RegLLVM dst, RegLLVM src, rword offset) {\n  int shift = 0;\n  if (offset != 0 && (offset % 4096) == 0) {\n    shift = 12;\n    offset = offset >> 12;\n    QBDI_REQUIRE_ABORT(offset < 4096, \"Must be a lower than 2**24 : {}\",\n                       offset);\n  } else {\n    QBDI_REQUIRE_ABORT(offset < 4096, \"Must be a lower than 4096 : {}\", offset);\n  }\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::SUBXri);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(shift));\n  return inst;\n}\n\nllvm::MCInst br(RegLLVM reg) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::BR);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  return inst;\n}\n\nllvm::MCInst blr(RegLLVM reg) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::BLR);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  return inst;\n}\n\nllvm::MCInst branch(rword offset) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::B);\n  inst.addOperand(llvm::MCOperand::createImm(offset / 4));\n  return inst;\n}\n\nllvm::MCInst cbz(RegLLVM reg, sword offset) {\n  QBDI_REQUIRE_ABORT(offset % 4 == 0,\n                     \"offset = SignExtend(imm19:'00', 64); (current : {})\",\n                     offset);\n  QBDI_REQUIRE_ABORT(-(1 << 20) <= offset and offset < (1 << 20),\n                     \"offset = SignExtend(imm19:'00', 64); (current : {})\",\n                     offset);\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::CBZX);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset / 4));\n  return inst;\n}\n\nllvm::MCInst ret(RegLLVM reg) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::RET);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  return inst;\n}\n\nllvm::MCInst adr(RegLLVM reg, sword offset) {\n  QBDI_REQUIRE_ABORT(-(1 << 20) <= offset and offset < (1 << 20),\n                     \"offset = SignExtend(imm21, 64); (current : {})\", offset);\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::ADR);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst adrp(RegLLVM reg, sword offset) {\n  QBDI_REQUIRE_ABORT(offset % (1 << 12) == 0,\n                     \"offset = SignExtend(imm21:Zeros(12)); (current : {})\",\n                     offset);\n  QBDI_REQUIRE_ABORT(-(1ll << 32) <= offset and offset < (1ll << 32),\n                     \"offset = SignExtend(imm21:Zeros(12)); (current : {})\",\n                     offset);\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::ADRP);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset / 0x1000));\n  return inst;\n}\n\nllvm::MCInst nop() {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::HINT);\n  inst.addOperand(llvm::MCOperand::createImm(0));\n  return inst;\n}\n\nllvm::MCInst ldr(RegLLVM dest, RegLLVM base, rword offset) {\n  sword soffset = static_cast<sword>(offset);\n  if (offset % 8 == 0 and soffset >= 0) {\n    return ldrui(dest, base, offset / 8);\n  }\n  return ldri(dest, base, soffset);\n}\n\nllvm::MCInst ldri(RegLLVM dest, RegLLVM base, sword offset) {\n\n  QBDI_REQUIRE_ABORT(-(1 << 8) <= offset and offset < (1 << 8),\n                     \"offset = SignExtend(imm9, 64); (current : {})\", offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDURXi);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst ldrui(RegLLVM dest, RegLLVM base, rword offset) {\n\n  QBDI_REQUIRE_ABORT(\n      offset < (1 << 12),\n      \"offset = LSL(ZeroExtend(imm12, 64), scale); (current : {})\", offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDRXui);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst ldrw(RegLLVM dest, RegLLVM base, rword offset) {\n  sword soffset = static_cast<sword>(offset);\n  if (offset % 4 == 0 and soffset >= 0) {\n    return ldrwui(dest, base, offset / 4);\n  }\n  return ldrwi(dest, base, soffset);\n}\n\nllvm::MCInst ldrwi(RegLLVM dest, RegLLVM base, sword offset) {\n\n  QBDI_REQUIRE_ABORT(-(1 << 8) <= offset and offset < (1 << 8),\n                     \"offset = SignExtend(imm9, 64); (current : {})\", offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDURWi);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst ldrwui(RegLLVM dest, RegLLVM base, rword offset) {\n\n  QBDI_REQUIRE_ABORT(\n      offset < (1 << 12),\n      \"offset = LSL(ZeroExtend(imm12, 64), scale); (current : {})\", offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDRWui);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst ldrh(RegLLVM dest, RegLLVM base, rword offset) {\n  sword soffset = static_cast<sword>(offset);\n  if (offset % 2 == 0 and soffset >= 0) {\n    return ldrhui(dest, base, offset / 2);\n  }\n  return ldrhi(dest, base, soffset);\n}\n\nllvm::MCInst ldrhi(RegLLVM dest, RegLLVM base, sword offset) {\n\n  QBDI_REQUIRE_ABORT(-(1 << 8) <= offset and offset < (1 << 8),\n                     \"offset = SignExtend(imm9, 64); (current : {})\", offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDURHHi);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst ldrhui(RegLLVM dest, RegLLVM base, rword offset) {\n\n  QBDI_REQUIRE_ABORT(offset < (1 << 12),\n                     \"offset = LSL(ZeroExtend(imm12, 64), 1); (current : {})\",\n                     offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDRHHui);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst ldrb(RegLLVM dest, RegLLVM base, rword offset) {\n\n  QBDI_REQUIRE_ABORT(offset < (1 << 12),\n                     \"offset = SignExtend(imm9, 64); (current : {})\", offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDRBBui);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst ldxrb(RegLLVM dest, RegLLVM addr) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDXRB);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(addr.getValue()));\n  return inst;\n}\n\nllvm::MCInst ldp(RegLLVM dest1, RegLLVM dest2, RegLLVM base, sword offset) {\n  QBDI_REQUIRE_ABORT(offset % 8 == 0, \"Must be a multiple of 8; (current : {})\",\n                     offset);\n  QBDI_REQUIRE_ABORT(-512 <= offset and offset <= 504,\n                     \"Must be in the range [-512, 504]; (current : {})\",\n                     offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDPXi);\n  inst.addOperand(llvm::MCOperand::createReg(dest1.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dest2.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset / 8));\n  return inst;\n}\n\nllvm::MCInst ldr_post_inc(RegLLVM dest, RegLLVM base, sword imm) {\n  QBDI_REQUIRE_ABORT(-256 <= imm and imm <= 255,\n                     \"Must be in the range [-512, 504]; (current : {})\", imm);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDRXpost);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  return inst;\n}\n\nllvm::MCInst ldp_post_inc(RegLLVM dest1, RegLLVM dest2, RegLLVM base,\n                          sword imm) {\n  QBDI_REQUIRE_ABORT(imm % 8 == 0, \"Must be a multiple of 8; (current : {})\",\n                     imm);\n  QBDI_REQUIRE_ABORT(-512 <= imm and imm <= 504,\n                     \"Must be in the range [-512, 504]; (current : {})\", imm);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::LDPXpost);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dest1.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dest2.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm / 8));\n  return inst;\n}\n\nllvm::MCInst str(RegLLVM src, RegLLVM base, rword offset) {\n  sword soffset = static_cast<sword>(offset);\n  if (offset % 8 == 0 and soffset >= 0) {\n    return strui(src, base, offset / 8);\n  }\n  return stri(src, base, soffset);\n}\n\nllvm::MCInst stri(RegLLVM src, RegLLVM base, sword offset) {\n  QBDI_REQUIRE_ABORT(-(1 << 8) <= offset and offset < (1 << 8),\n                     \"offset = SignExtend(imm9, 64); (current : {})\", offset);\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::STURXi);\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst strui(RegLLVM src, RegLLVM base, rword offset) {\n  QBDI_REQUIRE_ABORT(\n      offset < (1 << 12),\n      \"offset = LSL(ZeroExtend(imm12, 64), scale); (current : {})\", offset);\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::STRXui);\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  return inst;\n}\n\nllvm::MCInst stp(RegLLVM src1, RegLLVM src2, RegLLVM base, sword offset) {\n  QBDI_REQUIRE_ABORT(offset % 8 == 0, \"Must be a multiple of 8; (current : {})\",\n                     offset);\n  QBDI_REQUIRE_ABORT(-512 <= offset and offset <= 504,\n                     \"Must be in the range [-512, 504]; (current : {})\",\n                     offset);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::STPXi);\n  inst.addOperand(llvm::MCOperand::createReg(src1.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset / 8));\n  return inst;\n}\n\nllvm::MCInst str_pre_inc(RegLLVM reg, RegLLVM base, sword imm) {\n  QBDI_REQUIRE_ABORT(-256 <= imm and imm <= 255,\n                     \"Must be in the range [-512, 504]; (current : {})\", imm);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::STRXpre);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  return inst;\n}\n\nllvm::MCInst lsl(RegLLVM dst, RegLLVM src, size_t shift) {\n  QBDI_REQUIRE_ABORT(shift < 64, \"max shift of 64; (current : {})\", shift);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::UBFMXri);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  int64_t imms = 63 - shift;\n  int64_t immr = (imms + 1) % 64;\n\n  inst.addOperand(llvm::MCOperand::createImm(immr));\n  inst.addOperand(llvm::MCOperand::createImm(imms));\n  return inst;\n}\n\nllvm::MCInst lsr(RegLLVM dst, RegLLVM src, size_t shift) {\n  QBDI_REQUIRE_ABORT(shift < 64, \"max shift of 64; (current : {})\", shift);\n\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::UBFMXri);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(shift));\n  inst.addOperand(llvm::MCOperand::createImm(63));\n  return inst;\n}\n\nllvm::MCInst msr(unsigned sysdst, RegLLVM src) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::MSR);\n  inst.addOperand(llvm::MCOperand::createImm(sysdst));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  return inst;\n}\n\nllvm::MCInst mrs(RegLLVM dst, unsigned syssrc) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::MRS);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(syssrc));\n  return inst;\n}\n\nllvm::MCInst movrr(RegLLVM dst, RegLLVM src) {\n  if (dst == llvm::AArch64::SP or src == llvm::AArch64::SP) {\n    return addri(dst, src, 0);\n  }\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::ORRXrs);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::AArch64::XZR));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(0));\n  return inst;\n}\n\nllvm::MCInst movri(RegLLVM dst, uint16_t v) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::MOVZXi);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(v));\n  inst.addOperand(llvm::MCOperand::createImm(0));\n  return inst;\n}\n\nllvm::MCInst orrrs(RegLLVM dst, RegLLVM src1, RegLLVM src2, unsigned lshift) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::ORRXrs);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src1.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(lshift));\n  return inst;\n}\n\nllvm::MCInst brk(unsigned imm) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::BRK);\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  return inst;\n}\n\nllvm::MCInst hint(unsigned imm) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::HINT);\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  return inst;\n}\n\nllvm::MCInst xpacd(RegLLVM reg) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::XPACD);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  return inst;\n}\n\nllvm::MCInst xpaci(RegLLVM reg) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::XPACI);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  return inst;\n}\n\nllvm::MCInst autia(RegLLVM reg, RegLLVM ctx) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::AUTIA);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(ctx.getValue()));\n  return inst;\n}\n\nllvm::MCInst autib(RegLLVM reg, RegLLVM ctx) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::AUTIB);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(ctx.getValue()));\n  return inst;\n}\n\nllvm::MCInst autiza(RegLLVM reg) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::AUTIZA);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  return inst;\n}\n\nllvm::MCInst autizb(RegLLVM reg) {\n  llvm::MCInst inst;\n  inst.setOpcode(llvm::AArch64::AUTIZB);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  return inst;\n}\n\n// =================================================\n\nRelocatableInst::UniquePtr Ld1PostInc(RegLLVM regs, RegLLVM base) {\n  return NoReloc::unique(ld1_post_inc(regs, base));\n}\n\nRelocatableInst::UniquePtr St1PostInc(RegLLVM regs, RegLLVM base) {\n  return NoReloc::unique(st1_post_inc(regs, base));\n}\n\nRelocatableInst::UniquePtr Add(RegLLVM dst, RegLLVM src, Constant val) {\n  sword sval = static_cast<sword>(val);\n  if (sval < 0) {\n    return NoReloc::unique(subri(dst, src, -sval));\n  }\n  return NoReloc::unique(addri(dst, src, val));\n}\n\nRelocatableInst::UniquePtrVec Addc(RegLLVM dst, RegLLVM src, Constant val,\n                                   RegLLVM temp) {\n  QBDI_REQUIRE_ABORT(temp != src,\n                     \"Cannot add an 16 bits constant without 2 registers\");\n  sword sval = static_cast<sword>(val);\n  // if the value can be used in a add or sub, do it\n  if ((-4096 < sval) and (sval < 4096)) {\n    return conv_unique<RelocatableInst>(Add(dst, src, val));\n  }\n  if (sval < 0) {\n    return conv_unique<RelocatableInst>(LoadImm::unique(temp, -sval),\n                                        NoReloc::unique(subr(dst, src, temp)));\n  } else {\n    return conv_unique<RelocatableInst>(LoadImm::unique(temp, sval),\n                                        NoReloc::unique(addr(dst, src, temp)));\n  }\n}\n\nRelocatableInst::UniquePtr Addr(RegLLVM dst, RegLLVM src) {\n  return NoReloc::unique(addr(dst, src));\n}\n\nRelocatableInst::UniquePtr Addr(RegLLVM dst, RegLLVM src1, RegLLVM src2,\n                                ShiftExtendType type, Constant shift) {\n  return NoReloc::unique(addr(dst, src1, src2, type, shift));\n}\n\nRelocatableInst::UniquePtr Br(RegLLVM reg) { return NoReloc::unique(br(reg)); }\n\nRelocatableInst::UniquePtr Blr(RegLLVM reg) {\n  return NoReloc::unique(blr(reg));\n}\n\nRelocatableInst::UniquePtr Cbz(RegLLVM reg, Constant offset) {\n  return NoReloc::unique(cbz(reg, offset));\n}\n\nRelocatableInst::UniquePtr Ret() {\n  return NoReloc::unique(ret(llvm::AArch64::LR));\n}\n\nRelocatableInst::UniquePtr Adr(RegLLVM reg, rword offset) {\n  return NoReloc::unique(adr(reg, offset));\n}\n\nRelocatableInst::UniquePtr Adrp(RegLLVM reg, rword offset) {\n  QBDI_REQUIRE_ABORT(offset % 0x1000 == 0,\n                     \"Must be a multiple of 0x1000; (current : 0x{:x})\",\n                     offset);\n  return NoReloc::unique(adrp(reg, offset));\n}\n\nRelocatableInst::UniquePtr Nop() { return NoReloc::unique(nop()); }\n\nRelocatableInst::UniquePtr Ldr(RegLLVM reg, RegLLVM base, rword offset) {\n  return NoReloc::unique(ldr(reg, base, offset));\n}\n\nRelocatableInst::UniquePtr Ldrw(RegLLVM reg, RegLLVM base, rword offset) {\n  QBDI_REQUIRE(llvm::AArch64::X0 <= reg.getValue() and\n               reg.getValue() <= llvm::AArch64::X28);\n  // need a w register\n  RegLLVM wreg = llvm::getWRegFromXReg(reg.getValue());\n  return NoReloc::unique(ldrw(wreg, base, offset));\n}\n\nRelocatableInst::UniquePtr Ldrh(RegLLVM reg, RegLLVM base, rword offset) {\n  QBDI_REQUIRE(llvm::AArch64::X0 <= reg.getValue() and\n               reg.getValue() <= llvm::AArch64::X28);\n  // need a w register\n  RegLLVM wreg = llvm::getWRegFromXReg(reg.getValue());\n  return NoReloc::unique(ldrh(wreg, base, offset));\n}\n\nRelocatableInst::UniquePtr Ldrb(RegLLVM reg, RegLLVM base, rword offset) {\n  QBDI_REQUIRE(llvm::AArch64::X0 <= reg.getValue() and\n               reg.getValue() <= llvm::AArch64::X28);\n  // need a w register\n  RegLLVM wreg = llvm::getWRegFromXReg(reg.getValue());\n  return NoReloc::unique(ldrb(wreg, base, offset));\n}\n\nRelocatableInst::UniquePtr Ldr(RegLLVM reg, Offset offset) {\n  return LoadDataBlock::unique(reg, offset);\n}\n\nRelocatableInst::UniquePtr Ldxrb(RegLLVM dst, RegLLVM addr) {\n  QBDI_REQUIRE(llvm::AArch64::X0 <= dst.getValue() and\n               dst.getValue() <= llvm::AArch64::X28);\n  // need a w register\n  RegLLVM wreg = llvm::getWRegFromXReg(dst.getValue());\n  return NoReloc::unique(ldxrb(wreg, addr));\n}\n\nRelocatableInst::UniquePtr LdrPost(RegLLVM dest, RegLLVM base, Constant imm) {\n  return NoReloc::unique(ldr_post_inc(dest, base, imm));\n}\n\nRelocatableInst::UniquePtr Ldp(RegLLVM dest1, RegLLVM dest2, RegLLVM base,\n                               Offset offset) {\n  return NoReloc::unique(ldp(dest1, dest2, base, offset));\n}\n\nRelocatableInst::UniquePtr LdpPost(RegLLVM dest1, RegLLVM dest2, RegLLVM base,\n                                   Constant imm) {\n  return NoReloc::unique(ldp_post_inc(dest1, dest2, base, imm));\n}\n\nRelocatableInst::UniquePtr Str(RegLLVM reg, RegLLVM base, Offset offset) {\n  return NoReloc::unique(str(reg, base, offset));\n}\n\nRelocatableInst::UniquePtr Str(RegLLVM reg, Offset offset) {\n  return StoreDataBlock::unique(reg, offset);\n}\n\nRelocatableInst::UniquePtr StrPre(RegLLVM src, RegLLVM base, Constant imm) {\n  return NoReloc::unique(str_pre_inc(src, base, imm));\n}\n\nRelocatableInst::UniquePtr Stp(RegLLVM src1, RegLLVM src2, RegLLVM base,\n                               Offset offset) {\n  return NoReloc::unique(stp(src1, src2, base, offset));\n}\n\nRelocatableInst::UniquePtr Lsl(RegLLVM dst, RegLLVM src, Constant shift) {\n  return NoReloc::unique(lsl(dst, src, shift));\n}\n\nRelocatableInst::UniquePtr Lsr(RegLLVM dst, RegLLVM src, Constant shift) {\n  return NoReloc::unique(lsr(dst, src, shift));\n}\n\nRelocatableInst::UniquePtr ReadTPIDR(RegLLVM reg) {\n  return NoReloc::unique(mrs(reg, llvm::AArch64SysReg::TPIDR_EL0));\n}\n\nRelocatableInst::UniquePtr WriteTPIDR(RegLLVM reg) {\n  return NoReloc::unique(msr(llvm::AArch64SysReg::TPIDR_EL0, reg));\n}\n\nRelocatableInst::UniquePtr WriteSRinTPIDR() {\n  return SetScratchRegister::unique(msr(llvm::AArch64SysReg::TPIDR_EL0, 0), 1);\n}\n\nRelocatableInst::UniquePtr ReadNZCV(RegLLVM reg) {\n  return NoReloc::unique(mrs(reg, llvm::AArch64SysReg::NZCV));\n}\n\nRelocatableInst::UniquePtr WriteNZCV(RegLLVM reg) {\n  return NoReloc::unique(msr(llvm::AArch64SysReg::NZCV, reg));\n}\n\nRelocatableInst::UniquePtr ReadFPCR(RegLLVM reg) {\n  return NoReloc::unique(mrs(reg, llvm::AArch64SysReg::FPCR));\n}\n\nRelocatableInst::UniquePtr WriteFPCR(RegLLVM reg) {\n  return NoReloc::unique(msr(llvm::AArch64SysReg::FPCR, reg));\n}\n\nRelocatableInst::UniquePtr ReadFPSR(RegLLVM reg) {\n  return NoReloc::unique(mrs(reg, llvm::AArch64SysReg::FPSR));\n}\n\nRelocatableInst::UniquePtr WriteFPSR(RegLLVM reg) {\n  return NoReloc::unique(msr(llvm::AArch64SysReg::FPSR, reg));\n}\n\nRelocatableInst::UniquePtr Mov(RegLLVM dst, RegLLVM src) {\n  return MovReg::unique(dst, src);\n}\n\nRelocatableInst::UniquePtr Mov(RegLLVM dst, Constant constant) {\n  return LoadImm::unique(dst, constant);\n}\n\nRelocatableInst::UniquePtr Orrs(RegLLVM dst, RegLLVM src1, RegLLVM src2,\n                                Constant lshift) {\n  return NoReloc::unique(orrrs(dst, src1, src2, lshift));\n}\n\nRelocatableInst::UniquePtr BreakPoint() { return NoReloc::unique(brk(0)); }\n\nRelocatableInst::UniquePtr BTIc() { return NoReloc::unique(hint(0x22)); }\n\nRelocatableInst::UniquePtr BTIj() { return NoReloc::unique(hint(0x24)); }\n\nRelocatableInst::UniquePtr Xpacd(RegLLVM reg) {\n  return NoReloc::unique(xpacd(reg));\n}\n\nRelocatableInst::UniquePtr Xpaci(RegLLVM reg) {\n  return NoReloc::unique(xpaci(reg));\n}\n\nRelocatableInst::UniquePtr Autia(RegLLVM reg, RegLLVM ctx) {\n  return NoReloc::unique(autia(reg, ctx));\n}\n\nRelocatableInst::UniquePtr Autib(RegLLVM reg, RegLLVM ctx) {\n  return NoReloc::unique(autib(reg, ctx));\n}\n\nRelocatableInst::UniquePtr Autiza(RegLLVM reg) {\n  return NoReloc::unique(autiza(reg));\n}\n\nRelocatableInst::UniquePtr Autizb(RegLLVM reg) {\n  return NoReloc::unique(autizb(reg));\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/Layer2_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LAYER2_AARCH64_H\n#define LAYER2_AARCH64_H\n\n#include <memory>\n#include <stdint.h>\n#include <vector>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"QBDI/State.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass RelocatableInst;\n\n// proxy to llvm::AArch64_AM::ShiftExtendType\n// see MCTargetDesc/AArch64AddressingModes.h\nenum ShiftExtendType {\n  UXTB,\n  UXTH,\n  UXTW,\n  UXTX,\n  SXTB,\n  SXTH,\n  SXTW,\n  SXTX,\n};\nunsigned format_as(ShiftExtendType type);\n\n// low level layer 2\n\nllvm::MCInst ld1_post_inc(RegLLVM regs, RegLLVM base);\nllvm::MCInst st1_post_inc(RegLLVM regs, RegLLVM base);\n\nllvm::MCInst addr(RegLLVM dst, RegLLVM src);\nllvm::MCInst addr(RegLLVM dst, RegLLVM src1, RegLLVM src2);\nllvm::MCInst addr(RegLLVM dst, RegLLVM src1, RegLLVM src2, ShiftExtendType type,\n                  unsigned int shift);\nllvm::MCInst addri(RegLLVM dst, RegLLVM src, rword offset);\n\nllvm::MCInst subr(RegLLVM dst, RegLLVM src);\nllvm::MCInst subr(RegLLVM dst, RegLLVM src1, RegLLVM src2);\nllvm::MCInst subri(RegLLVM dst, RegLLVM src, rword offset);\n\nllvm::MCInst br(RegLLVM reg);\nllvm::MCInst blr(RegLLVM reg);\nllvm::MCInst branch(rword offset);\nllvm::MCInst cbz(RegLLVM reg, sword offset);\nllvm::MCInst ret(RegLLVM reg);\nllvm::MCInst adr(RegLLVM reg, sword offset);\nllvm::MCInst adrp(RegLLVM reg, sword offset = 0);\nllvm::MCInst nop();\n\nllvm::MCInst ldr(RegLLVM dest, RegLLVM base, rword offset);\nllvm::MCInst ldri(RegLLVM dest, RegLLVM base, sword offset);\nllvm::MCInst ldrui(RegLLVM dest, RegLLVM base, rword offset);\nllvm::MCInst ldrw(RegLLVM dest, RegLLVM base, rword offset);\nllvm::MCInst ldrwi(RegLLVM dest, RegLLVM base, sword offset);\nllvm::MCInst ldrwui(RegLLVM dest, RegLLVM base, rword offset);\nllvm::MCInst ldrh(RegLLVM dest, RegLLVM base, rword offset);\nllvm::MCInst ldrhi(RegLLVM dest, RegLLVM base, sword offset);\nllvm::MCInst ldrhui(RegLLVM dest, RegLLVM base, rword offset);\nllvm::MCInst ldrb(RegLLVM dest, RegLLVM base, rword offset);\nllvm::MCInst ldxrb(RegLLVM dest, RegLLVM addr);\nllvm::MCInst ldp(RegLLVM dest1, RegLLVM dest2, RegLLVM base, sword offset);\nllvm::MCInst ldr_post_inc(RegLLVM dest, RegLLVM base, sword imm);\nllvm::MCInst ldp_post_inc(RegLLVM dest1, RegLLVM dest2, RegLLVM base,\n                          sword imm);\n\nllvm::MCInst str(RegLLVM src, RegLLVM base, rword offset);\nllvm::MCInst stri(RegLLVM src, RegLLVM base, sword offset);\nllvm::MCInst strui(RegLLVM src, RegLLVM base, rword offset);\nllvm::MCInst stp(RegLLVM src1, RegLLVM src2, RegLLVM base, sword offset);\nllvm::MCInst str_pre_inc(RegLLVM reg, RegLLVM base, sword imm);\n\nllvm::MCInst lsl(RegLLVM dst, RegLLVM src, size_t shift);\nllvm::MCInst lsr(RegLLVM dst, RegLLVM src, size_t shift);\n\nllvm::MCInst msr(unsigned sysdst, RegLLVM src);\nllvm::MCInst mrs(RegLLVM dst, unsigned syssrc);\n\nllvm::MCInst movrr(RegLLVM dst, RegLLVM src);\nllvm::MCInst movri(RegLLVM dst, uint16_t v);\nllvm::MCInst orrrs(RegLLVM dst, RegLLVM src1, RegLLVM src2, unsigned lshift);\n\nllvm::MCInst brk(unsigned imm);\nllvm::MCInst hint(unsigned imm);\n\nllvm::MCInst xpacd(RegLLVM reg);\nllvm::MCInst xpaci(RegLLVM reg);\n\nllvm::MCInst autia(RegLLVM reg, RegLLVM ctx);\nllvm::MCInst autib(RegLLVM reg, RegLLVM ctx);\nllvm::MCInst autiza(RegLLVM reg);\nllvm::MCInst autizb(RegLLVM reg);\n\n// high level layer 2\n\nstd::unique_ptr<RelocatableInst> Ld1PostInc(RegLLVM regs, RegLLVM base);\nstd::unique_ptr<RelocatableInst> St1PostInc(RegLLVM regs, RegLLVM base);\n\nstd::unique_ptr<RelocatableInst> Add(RegLLVM dst, RegLLVM src, Constant val);\nstd::vector<std::unique_ptr<RelocatableInst>> Addc(RegLLVM dst, RegLLVM src,\n                                                   Constant val, RegLLVM temp);\n\nstd::unique_ptr<RelocatableInst> Addr(RegLLVM dst, RegLLVM src);\nstd::unique_ptr<RelocatableInst> Addr(RegLLVM dst, RegLLVM src1, RegLLVM src2,\n                                      ShiftExtendType type, Constant shift);\n\nstd::unique_ptr<RelocatableInst> Br(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> Blr(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> Cbz(RegLLVM reg, Constant offset);\nstd::unique_ptr<RelocatableInst> Ret();\nstd::unique_ptr<RelocatableInst> Adr(RegLLVM reg, rword offset);\nstd::unique_ptr<RelocatableInst> Adrp(RegLLVM reg, rword offset);\nstd::unique_ptr<RelocatableInst> Nop();\n\nstd::unique_ptr<RelocatableInst> Ldr(RegLLVM reg, RegLLVM base, rword offset);\nstd::unique_ptr<RelocatableInst> Ldrw(RegLLVM reg, RegLLVM base, rword offset);\nstd::unique_ptr<RelocatableInst> Ldrh(RegLLVM reg, RegLLVM base, rword offset);\nstd::unique_ptr<RelocatableInst> Ldrb(RegLLVM reg, RegLLVM base, rword offset);\nstd::unique_ptr<RelocatableInst> Ldr(RegLLVM reg, Offset offset);\nstd::unique_ptr<RelocatableInst> Ldxrb(RegLLVM dest, RegLLVM addr);\nstd::unique_ptr<RelocatableInst> LdrPost(RegLLVM dest, RegLLVM base,\n                                         Constant imm);\nstd::unique_ptr<RelocatableInst> Ldp(RegLLVM dest1, RegLLVM dest2, RegLLVM base,\n                                     Offset offset);\nstd::unique_ptr<RelocatableInst> LdpPost(RegLLVM dest1, RegLLVM dest2,\n                                         RegLLVM base, Constant imm);\n\nstd::unique_ptr<RelocatableInst> Str(RegLLVM reg, RegLLVM base, Offset offset);\nstd::unique_ptr<RelocatableInst> Str(RegLLVM reg, Offset offset);\nstd::unique_ptr<RelocatableInst> StrPre(RegLLVM reg, RegLLVM base,\n                                        Constant imm);\nstd::unique_ptr<RelocatableInst> Stp(RegLLVM src1, RegLLVM src2, RegLLVM base,\n                                     Offset offset);\n\nstd::unique_ptr<RelocatableInst> Lsl(RegLLVM dst, RegLLVM src, Constant shift);\nstd::unique_ptr<RelocatableInst> Lsr(RegLLVM dst, RegLLVM src, Constant shift);\n\nstd::unique_ptr<RelocatableInst> ReadTPIDR(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> WriteTPIDR(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> WriteSRinTPIDR();\n\nstd::unique_ptr<RelocatableInst> ReadNZCV(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> WriteNZCV(RegLLVM reg);\n\nstd::unique_ptr<RelocatableInst> ReadFPCR(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> WriteFPCR(RegLLVM reg);\n\nstd::unique_ptr<RelocatableInst> ReadFPSR(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> WriteFPSR(RegLLVM reg);\n\nstd::unique_ptr<RelocatableInst> Mov(RegLLVM dst, RegLLVM src);\nstd::unique_ptr<RelocatableInst> Mov(RegLLVM dst, Constant constant);\nstd::unique_ptr<RelocatableInst> Orrs(RegLLVM dst, RegLLVM src1, RegLLVM src2,\n                                      Constant lshift);\n\nstd::unique_ptr<RelocatableInst> BreakPoint();\nstd::unique_ptr<RelocatableInst> BTIc();\nstd::unique_ptr<RelocatableInst> BTIj();\n\nstd::unique_ptr<RelocatableInst> Xpacd(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> Xpaci(RegLLVM reg);\n\nstd::unique_ptr<RelocatableInst> Autia(RegLLVM reg, RegLLVM ctx);\nstd::unique_ptr<RelocatableInst> Autib(RegLLVM reg, RegLLVM ctx);\nstd::unique_ptr<RelocatableInst> Autiza(RegLLVM reg);\nstd::unique_ptr<RelocatableInst> Autizb(RegLLVM reg);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/MemoryAccess_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <memory>\n#include <stddef.h>\n#include <stdint.h>\n#include <utility>\n#include <vector>\n\n#include \"llvm/ADT/ArrayRef.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n\n#include \"AArch64InstrInfo.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n\n#include \"devVariable.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/AARCH64/InstInfo_AARCH64.h\"\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/MemoryAccess_AARCH64.h\"\n#include \"Patch/AARCH64/PatchCondition_AARCH64.h\"\n#include \"Patch/AARCH64/PatchGenerator_AARCH64.h\"\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/MemoryAccess.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Callback.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n// PatchGenerator MemoryAccess Address/ReadValue/WriteValue Generator\n// ==================================================================\n\nnamespace {\n\ntypedef RelocatableInst::UniquePtrVec(AddressGenFn)(const Patch &patch,\n                                                    bool writeAccess, Reg dest);\n\n/* Address in a register\n * =====================\n */\nRelocatableInst::UniquePtrVec ADDR_REGISTER_FN(const Patch &patch,\n                                               bool writeAccess, Reg dest,\n                                               unsigned operandOff) {\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff, patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff).getReg();\n  return conv_unique<RelocatableInst>(MovReg::unique(dest, addrReg));\n}\n\n// address on the 2nd operand\nconstexpr unsigned ADDR_REGISTER_2_TABLE[] = {\n    // ld1\t{ v28.4s }, [x0]\n    // clang-format off\n    llvm::AArch64::GCSSTR,\n    llvm::AArch64::GCSSTTR,\n    llvm::AArch64::LD1Fourv16b,\n    llvm::AArch64::LD1Fourv1d,\n    llvm::AArch64::LD1Fourv2d,\n    llvm::AArch64::LD1Fourv2s,\n    llvm::AArch64::LD1Fourv4h,\n    llvm::AArch64::LD1Fourv4s,\n    llvm::AArch64::LD1Fourv8b,\n    llvm::AArch64::LD1Fourv8h,\n    llvm::AArch64::LD1Onev16b,\n    llvm::AArch64::LD1Onev1d,\n    llvm::AArch64::LD1Onev2d,\n    llvm::AArch64::LD1Onev2s,\n    llvm::AArch64::LD1Onev4h,\n    llvm::AArch64::LD1Onev4s,\n    llvm::AArch64::LD1Onev8b,\n    llvm::AArch64::LD1Onev8h,\n    llvm::AArch64::LD1Rv16b,\n    llvm::AArch64::LD1Rv1d,\n    llvm::AArch64::LD1Rv2d,\n    llvm::AArch64::LD1Rv2s,\n    llvm::AArch64::LD1Rv4h,\n    llvm::AArch64::LD1Rv4s,\n    llvm::AArch64::LD1Rv8b,\n    llvm::AArch64::LD1Rv8h,\n    llvm::AArch64::LD1Threev16b,\n    llvm::AArch64::LD1Threev1d,\n    llvm::AArch64::LD1Threev2d,\n    llvm::AArch64::LD1Threev2s,\n    llvm::AArch64::LD1Threev4h,\n    llvm::AArch64::LD1Threev4s,\n    llvm::AArch64::LD1Threev8b,\n    llvm::AArch64::LD1Threev8h,\n    llvm::AArch64::LD1Twov16b,\n    llvm::AArch64::LD1Twov1d,\n    llvm::AArch64::LD1Twov2d,\n    llvm::AArch64::LD1Twov2s,\n    llvm::AArch64::LD1Twov4h,\n    llvm::AArch64::LD1Twov4s,\n    llvm::AArch64::LD1Twov8b,\n    llvm::AArch64::LD1Twov8h,\n    llvm::AArch64::LD2Rv16b,\n    llvm::AArch64::LD2Rv1d,\n    llvm::AArch64::LD2Rv2d,\n    llvm::AArch64::LD2Rv2s,\n    llvm::AArch64::LD2Rv4h,\n    llvm::AArch64::LD2Rv4s,\n    llvm::AArch64::LD2Rv8b,\n    llvm::AArch64::LD2Rv8h,\n    llvm::AArch64::LD2Twov16b,\n    llvm::AArch64::LD2Twov2d,\n    llvm::AArch64::LD2Twov2s,\n    llvm::AArch64::LD2Twov4h,\n    llvm::AArch64::LD2Twov4s,\n    llvm::AArch64::LD2Twov8b,\n    llvm::AArch64::LD2Twov8h,\n    llvm::AArch64::LD3Rv16b,\n    llvm::AArch64::LD3Rv1d,\n    llvm::AArch64::LD3Rv2d,\n    llvm::AArch64::LD3Rv2s,\n    llvm::AArch64::LD3Rv4h,\n    llvm::AArch64::LD3Rv4s,\n    llvm::AArch64::LD3Rv8b,\n    llvm::AArch64::LD3Rv8h,\n    llvm::AArch64::LD3Threev16b,\n    llvm::AArch64::LD3Threev2d,\n    llvm::AArch64::LD3Threev2s,\n    llvm::AArch64::LD3Threev4h,\n    llvm::AArch64::LD3Threev4s,\n    llvm::AArch64::LD3Threev8b,\n    llvm::AArch64::LD3Threev8h,\n    llvm::AArch64::LD4Fourv16b,\n    llvm::AArch64::LD4Fourv2d,\n    llvm::AArch64::LD4Fourv2s,\n    llvm::AArch64::LD4Fourv4h,\n    llvm::AArch64::LD4Fourv4s,\n    llvm::AArch64::LD4Fourv8b,\n    llvm::AArch64::LD4Fourv8h,\n    llvm::AArch64::LD4Rv16b,\n    llvm::AArch64::LD4Rv1d,\n    llvm::AArch64::LD4Rv2d,\n    llvm::AArch64::LD4Rv2s,\n    llvm::AArch64::LD4Rv4h,\n    llvm::AArch64::LD4Rv4s,\n    llvm::AArch64::LD4Rv8b,\n    llvm::AArch64::LD4Rv8h,\n    llvm::AArch64::LD64B,\n    llvm::AArch64::LDAPRB,\n    llvm::AArch64::LDAPRH,\n    llvm::AArch64::LDAPRW,\n    llvm::AArch64::LDAPRX,\n    llvm::AArch64::LDARB,\n    llvm::AArch64::LDARH,\n    llvm::AArch64::LDARW,\n    llvm::AArch64::LDARX,\n    llvm::AArch64::LDAXRB,\n    llvm::AArch64::LDAXRH,\n    llvm::AArch64::LDAXRW,\n    llvm::AArch64::LDAXRX,\n    llvm::AArch64::LDLARB,\n    llvm::AArch64::LDLARH,\n    llvm::AArch64::LDLARW,\n    llvm::AArch64::LDLARX,\n    llvm::AArch64::LDXRB,\n    llvm::AArch64::LDXRH,\n    llvm::AArch64::LDXRW,\n    llvm::AArch64::LDXRX,\n    llvm::AArch64::ST1Fourv16b,\n    llvm::AArch64::ST1Fourv1d,\n    llvm::AArch64::ST1Fourv2d,\n    llvm::AArch64::ST1Fourv2s,\n    llvm::AArch64::ST1Fourv4h,\n    llvm::AArch64::ST1Fourv4s,\n    llvm::AArch64::ST1Fourv8b,\n    llvm::AArch64::ST1Fourv8h,\n    llvm::AArch64::ST1Onev16b,\n    llvm::AArch64::ST1Onev1d,\n    llvm::AArch64::ST1Onev2d,\n    llvm::AArch64::ST1Onev2s,\n    llvm::AArch64::ST1Onev4h,\n    llvm::AArch64::ST1Onev4s,\n    llvm::AArch64::ST1Onev8b,\n    llvm::AArch64::ST1Onev8h,\n    llvm::AArch64::ST1Threev16b,\n    llvm::AArch64::ST1Threev1d,\n    llvm::AArch64::ST1Threev2d,\n    llvm::AArch64::ST1Threev2s,\n    llvm::AArch64::ST1Threev4h,\n    llvm::AArch64::ST1Threev4s,\n    llvm::AArch64::ST1Threev8b,\n    llvm::AArch64::ST1Threev8h,\n    llvm::AArch64::ST1Twov16b,\n    llvm::AArch64::ST1Twov1d,\n    llvm::AArch64::ST1Twov2d,\n    llvm::AArch64::ST1Twov2s,\n    llvm::AArch64::ST1Twov4h,\n    llvm::AArch64::ST1Twov4s,\n    llvm::AArch64::ST1Twov8b,\n    llvm::AArch64::ST1Twov8h,\n    llvm::AArch64::ST2Twov16b,\n    llvm::AArch64::ST2Twov2d,\n    llvm::AArch64::ST2Twov2s,\n    llvm::AArch64::ST2Twov4h,\n    llvm::AArch64::ST2Twov4s,\n    llvm::AArch64::ST2Twov8b,\n    llvm::AArch64::ST2Twov8h,\n    llvm::AArch64::ST3Threev16b,\n    llvm::AArch64::ST3Threev2d,\n    llvm::AArch64::ST3Threev2s,\n    llvm::AArch64::ST3Threev4h,\n    llvm::AArch64::ST3Threev4s,\n    llvm::AArch64::ST3Threev8b,\n    llvm::AArch64::ST3Threev8h,\n    llvm::AArch64::ST4Fourv16b,\n    llvm::AArch64::ST4Fourv2d,\n    llvm::AArch64::ST4Fourv2s,\n    llvm::AArch64::ST4Fourv4h,\n    llvm::AArch64::ST4Fourv4s,\n    llvm::AArch64::ST4Fourv8b,\n    llvm::AArch64::ST4Fourv8h,\n    llvm::AArch64::ST64B,\n    llvm::AArch64::STLLRB,\n    llvm::AArch64::STLLRH,\n    llvm::AArch64::STLLRW,\n    llvm::AArch64::STLLRX,\n    llvm::AArch64::STLRB,\n    llvm::AArch64::STLRH,\n    llvm::AArch64::STLRW,\n    llvm::AArch64::STLRX,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_2_SIZE =\n    sizeof(ADDR_REGISTER_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REGISTER_2_FN(const Patch &patch,\n                                                 bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_FN(patch, writeAccess, dest, 1);\n}\n\n// address on the 3rd operand\nconstexpr unsigned ADDR_REGISTER_3_TABLE[] = {\n    // swpb w0, w1, [x2]\n    // clang-format off\n    llvm::AArch64::LD1Fourv16b_POST,\n    llvm::AArch64::LD1Fourv1d_POST,\n    llvm::AArch64::LD1Fourv2d_POST,\n    llvm::AArch64::LD1Fourv2s_POST,\n    llvm::AArch64::LD1Fourv4h_POST,\n    llvm::AArch64::LD1Fourv4s_POST,\n    llvm::AArch64::LD1Fourv8b_POST,\n    llvm::AArch64::LD1Fourv8h_POST,\n    llvm::AArch64::LD1Onev16b_POST,\n    llvm::AArch64::LD1Onev1d_POST,\n    llvm::AArch64::LD1Onev2d_POST,\n    llvm::AArch64::LD1Onev2s_POST,\n    llvm::AArch64::LD1Onev4h_POST,\n    llvm::AArch64::LD1Onev4s_POST,\n    llvm::AArch64::LD1Onev8b_POST,\n    llvm::AArch64::LD1Onev8h_POST,\n    llvm::AArch64::LD1Rv16b_POST,\n    llvm::AArch64::LD1Rv1d_POST,\n    llvm::AArch64::LD1Rv2d_POST,\n    llvm::AArch64::LD1Rv2s_POST,\n    llvm::AArch64::LD1Rv4h_POST,\n    llvm::AArch64::LD1Rv4s_POST,\n    llvm::AArch64::LD1Rv8b_POST,\n    llvm::AArch64::LD1Rv8h_POST,\n    llvm::AArch64::LD1Threev16b_POST,\n    llvm::AArch64::LD1Threev1d_POST,\n    llvm::AArch64::LD1Threev2d_POST,\n    llvm::AArch64::LD1Threev2s_POST,\n    llvm::AArch64::LD1Threev4h_POST,\n    llvm::AArch64::LD1Threev4s_POST,\n    llvm::AArch64::LD1Threev8b_POST,\n    llvm::AArch64::LD1Threev8h_POST,\n    llvm::AArch64::LD1Twov16b_POST,\n    llvm::AArch64::LD1Twov1d_POST,\n    llvm::AArch64::LD1Twov2d_POST,\n    llvm::AArch64::LD1Twov2s_POST,\n    llvm::AArch64::LD1Twov4h_POST,\n    llvm::AArch64::LD1Twov4s_POST,\n    llvm::AArch64::LD1Twov8b_POST,\n    llvm::AArch64::LD1Twov8h_POST,\n    llvm::AArch64::LD2Rv16b_POST,\n    llvm::AArch64::LD2Rv1d_POST,\n    llvm::AArch64::LD2Rv2d_POST,\n    llvm::AArch64::LD2Rv2s_POST,\n    llvm::AArch64::LD2Rv4h_POST,\n    llvm::AArch64::LD2Rv4s_POST,\n    llvm::AArch64::LD2Rv8b_POST,\n    llvm::AArch64::LD2Rv8h_POST,\n    llvm::AArch64::LD2Twov16b_POST,\n    llvm::AArch64::LD2Twov2d_POST,\n    llvm::AArch64::LD2Twov2s_POST,\n    llvm::AArch64::LD2Twov4h_POST,\n    llvm::AArch64::LD2Twov4s_POST,\n    llvm::AArch64::LD2Twov8b_POST,\n    llvm::AArch64::LD2Twov8h_POST,\n    llvm::AArch64::LD3Rv16b_POST,\n    llvm::AArch64::LD3Rv1d_POST,\n    llvm::AArch64::LD3Rv2d_POST,\n    llvm::AArch64::LD3Rv2s_POST,\n    llvm::AArch64::LD3Rv4h_POST,\n    llvm::AArch64::LD3Rv4s_POST,\n    llvm::AArch64::LD3Rv8b_POST,\n    llvm::AArch64::LD3Rv8h_POST,\n    llvm::AArch64::LD3Threev16b_POST,\n    llvm::AArch64::LD3Threev2d_POST,\n    llvm::AArch64::LD3Threev2s_POST,\n    llvm::AArch64::LD3Threev4h_POST,\n    llvm::AArch64::LD3Threev4s_POST,\n    llvm::AArch64::LD3Threev8b_POST,\n    llvm::AArch64::LD3Threev8h_POST,\n    llvm::AArch64::LD4Fourv16b_POST,\n    llvm::AArch64::LD4Fourv2d_POST,\n    llvm::AArch64::LD4Fourv2s_POST,\n    llvm::AArch64::LD4Fourv4h_POST,\n    llvm::AArch64::LD4Fourv4s_POST,\n    llvm::AArch64::LD4Fourv8b_POST,\n    llvm::AArch64::LD4Fourv8h_POST,\n    llvm::AArch64::LD4Rv16b_POST,\n    llvm::AArch64::LD4Rv1d_POST,\n    llvm::AArch64::LD4Rv2d_POST,\n    llvm::AArch64::LD4Rv2s_POST,\n    llvm::AArch64::LD4Rv4h_POST,\n    llvm::AArch64::LD4Rv4s_POST,\n    llvm::AArch64::LD4Rv8b_POST,\n    llvm::AArch64::LD4Rv8h_POST,\n    llvm::AArch64::LDADDAB,\n    llvm::AArch64::LDADDAH,\n    llvm::AArch64::LDADDALB,\n    llvm::AArch64::LDADDALH,\n    llvm::AArch64::LDADDALW,\n    llvm::AArch64::LDADDALX,\n    llvm::AArch64::LDADDAW,\n    llvm::AArch64::LDADDAX,\n    llvm::AArch64::LDADDB,\n    llvm::AArch64::LDADDH,\n    llvm::AArch64::LDADDLB,\n    llvm::AArch64::LDADDLH,\n    llvm::AArch64::LDADDLW,\n    llvm::AArch64::LDADDLX,\n    llvm::AArch64::LDADDW,\n    llvm::AArch64::LDADDX,\n    llvm::AArch64::LDAPRWpost,\n    llvm::AArch64::LDAPRXpost,\n    llvm::AArch64::LDAXPW,\n    llvm::AArch64::LDAXPX,\n    llvm::AArch64::LDCLRAB,\n    llvm::AArch64::LDCLRAH,\n    llvm::AArch64::LDCLRALB,\n    llvm::AArch64::LDCLRALH,\n    llvm::AArch64::LDCLRALW,\n    llvm::AArch64::LDCLRALX,\n    llvm::AArch64::LDCLRAW,\n    llvm::AArch64::LDCLRAX,\n    llvm::AArch64::LDCLRB,\n    llvm::AArch64::LDCLRH,\n    llvm::AArch64::LDCLRLB,\n    llvm::AArch64::LDCLRLH,\n    llvm::AArch64::LDCLRLW,\n    llvm::AArch64::LDCLRLX,\n    llvm::AArch64::LDCLRW,\n    llvm::AArch64::LDCLRX,\n    llvm::AArch64::LDEORAB,\n    llvm::AArch64::LDEORAH,\n    llvm::AArch64::LDEORALB,\n    llvm::AArch64::LDEORALH,\n    llvm::AArch64::LDEORALW,\n    llvm::AArch64::LDEORALX,\n    llvm::AArch64::LDEORAW,\n    llvm::AArch64::LDEORAX,\n    llvm::AArch64::LDEORB,\n    llvm::AArch64::LDEORH,\n    llvm::AArch64::LDEORLB,\n    llvm::AArch64::LDEORLH,\n    llvm::AArch64::LDEORLW,\n    llvm::AArch64::LDEORLX,\n    llvm::AArch64::LDEORW,\n    llvm::AArch64::LDEORX,\n    llvm::AArch64::LDIAPPW,\n    llvm::AArch64::LDIAPPX,\n    llvm::AArch64::LDRBBpost,\n    llvm::AArch64::LDRBpost,\n    llvm::AArch64::LDRDpost,\n    llvm::AArch64::LDRHHpost,\n    llvm::AArch64::LDRHpost,\n    llvm::AArch64::LDRQpost,\n    llvm::AArch64::LDRSBWpost,\n    llvm::AArch64::LDRSBXpost,\n    llvm::AArch64::LDRSHWpost,\n    llvm::AArch64::LDRSHXpost,\n    llvm::AArch64::LDRSWpost,\n    llvm::AArch64::LDRSpost,\n    llvm::AArch64::LDRWpost,\n    llvm::AArch64::LDRXpost,\n    llvm::AArch64::LDSETAB,\n    llvm::AArch64::LDSETAH,\n    llvm::AArch64::LDSETALB,\n    llvm::AArch64::LDSETALH,\n    llvm::AArch64::LDSETALW,\n    llvm::AArch64::LDSETALX,\n    llvm::AArch64::LDSETAW,\n    llvm::AArch64::LDSETAX,\n    llvm::AArch64::LDSETB,\n    llvm::AArch64::LDSETH,\n    llvm::AArch64::LDSETLB,\n    llvm::AArch64::LDSETLH,\n    llvm::AArch64::LDSETLW,\n    llvm::AArch64::LDSETLX,\n    llvm::AArch64::LDSETW,\n    llvm::AArch64::LDSETX,\n    llvm::AArch64::LDSMAXAB,\n    llvm::AArch64::LDSMAXAH,\n    llvm::AArch64::LDSMAXALB,\n    llvm::AArch64::LDSMAXALH,\n    llvm::AArch64::LDSMAXALW,\n    llvm::AArch64::LDSMAXALX,\n    llvm::AArch64::LDSMAXAW,\n    llvm::AArch64::LDSMAXAX,\n    llvm::AArch64::LDSMAXB,\n    llvm::AArch64::LDSMAXH,\n    llvm::AArch64::LDSMAXLB,\n    llvm::AArch64::LDSMAXLH,\n    llvm::AArch64::LDSMAXLW,\n    llvm::AArch64::LDSMAXLX,\n    llvm::AArch64::LDSMAXW,\n    llvm::AArch64::LDSMAXX,\n    llvm::AArch64::LDSMINAB,\n    llvm::AArch64::LDSMINAH,\n    llvm::AArch64::LDSMINALB,\n    llvm::AArch64::LDSMINALH,\n    llvm::AArch64::LDSMINALW,\n    llvm::AArch64::LDSMINALX,\n    llvm::AArch64::LDSMINAW,\n    llvm::AArch64::LDSMINAX,\n    llvm::AArch64::LDSMINB,\n    llvm::AArch64::LDSMINH,\n    llvm::AArch64::LDSMINLB,\n    llvm::AArch64::LDSMINLH,\n    llvm::AArch64::LDSMINLW,\n    llvm::AArch64::LDSMINLX,\n    llvm::AArch64::LDSMINW,\n    llvm::AArch64::LDSMINX,\n    llvm::AArch64::LDUMAXAB,\n    llvm::AArch64::LDUMAXAH,\n    llvm::AArch64::LDUMAXALB,\n    llvm::AArch64::LDUMAXALH,\n    llvm::AArch64::LDUMAXALW,\n    llvm::AArch64::LDUMAXALX,\n    llvm::AArch64::LDUMAXAW,\n    llvm::AArch64::LDUMAXAX,\n    llvm::AArch64::LDUMAXB,\n    llvm::AArch64::LDUMAXH,\n    llvm::AArch64::LDUMAXLB,\n    llvm::AArch64::LDUMAXLH,\n    llvm::AArch64::LDUMAXLW,\n    llvm::AArch64::LDUMAXLX,\n    llvm::AArch64::LDUMAXW,\n    llvm::AArch64::LDUMAXX,\n    llvm::AArch64::LDUMINAB,\n    llvm::AArch64::LDUMINAH,\n    llvm::AArch64::LDUMINALB,\n    llvm::AArch64::LDUMINALH,\n    llvm::AArch64::LDUMINALW,\n    llvm::AArch64::LDUMINALX,\n    llvm::AArch64::LDUMINAW,\n    llvm::AArch64::LDUMINAX,\n    llvm::AArch64::LDUMINB,\n    llvm::AArch64::LDUMINH,\n    llvm::AArch64::LDUMINLB,\n    llvm::AArch64::LDUMINLH,\n    llvm::AArch64::LDUMINLW,\n    llvm::AArch64::LDUMINLX,\n    llvm::AArch64::LDUMINW,\n    llvm::AArch64::LDUMINX,\n    llvm::AArch64::LDXPW,\n    llvm::AArch64::LDXPX,\n    llvm::AArch64::RCWCLR,\n    llvm::AArch64::RCWCLRA,\n    llvm::AArch64::RCWCLRAL,\n    llvm::AArch64::RCWCLRL,\n    llvm::AArch64::RCWCLRS,\n    llvm::AArch64::RCWCLRSA,\n    llvm::AArch64::RCWCLRSAL,\n    llvm::AArch64::RCWCLRSL,\n    llvm::AArch64::RCWSET,\n    llvm::AArch64::RCWSETA,\n    llvm::AArch64::RCWSETAL,\n    llvm::AArch64::RCWSETL,\n    llvm::AArch64::RCWSETS,\n    llvm::AArch64::RCWSETSA,\n    llvm::AArch64::RCWSETSAL,\n    llvm::AArch64::RCWSETSL,\n    llvm::AArch64::RCWSWP,\n    llvm::AArch64::RCWSWPA,\n    llvm::AArch64::RCWSWPAL,\n    llvm::AArch64::RCWSWPL,\n    llvm::AArch64::RCWSWPS,\n    llvm::AArch64::RCWSWPSA,\n    llvm::AArch64::RCWSWPSAL,\n    llvm::AArch64::RCWSWPSL,\n    llvm::AArch64::ST1Fourv16b_POST,\n    llvm::AArch64::ST1Fourv1d_POST,\n    llvm::AArch64::ST1Fourv2d_POST,\n    llvm::AArch64::ST1Fourv2s_POST,\n    llvm::AArch64::ST1Fourv4h_POST,\n    llvm::AArch64::ST1Fourv4s_POST,\n    llvm::AArch64::ST1Fourv8b_POST,\n    llvm::AArch64::ST1Fourv8h_POST,\n    llvm::AArch64::ST1Onev16b_POST,\n    llvm::AArch64::ST1Onev1d_POST,\n    llvm::AArch64::ST1Onev2d_POST,\n    llvm::AArch64::ST1Onev2s_POST,\n    llvm::AArch64::ST1Onev4h_POST,\n    llvm::AArch64::ST1Onev4s_POST,\n    llvm::AArch64::ST1Onev8b_POST,\n    llvm::AArch64::ST1Onev8h_POST,\n    llvm::AArch64::ST1Threev16b_POST,\n    llvm::AArch64::ST1Threev1d_POST,\n    llvm::AArch64::ST1Threev2d_POST,\n    llvm::AArch64::ST1Threev2s_POST,\n    llvm::AArch64::ST1Threev4h_POST,\n    llvm::AArch64::ST1Threev4s_POST,\n    llvm::AArch64::ST1Threev8b_POST,\n    llvm::AArch64::ST1Threev8h_POST,\n    llvm::AArch64::ST1Twov16b_POST,\n    llvm::AArch64::ST1Twov1d_POST,\n    llvm::AArch64::ST1Twov2d_POST,\n    llvm::AArch64::ST1Twov2s_POST,\n    llvm::AArch64::ST1Twov4h_POST,\n    llvm::AArch64::ST1Twov4s_POST,\n    llvm::AArch64::ST1Twov8b_POST,\n    llvm::AArch64::ST1Twov8h_POST,\n    llvm::AArch64::ST1i16,\n    llvm::AArch64::ST1i32,\n    llvm::AArch64::ST1i64,\n    llvm::AArch64::ST1i8,\n    llvm::AArch64::ST2Twov16b_POST,\n    llvm::AArch64::ST2Twov2d_POST,\n    llvm::AArch64::ST2Twov2s_POST,\n    llvm::AArch64::ST2Twov4h_POST,\n    llvm::AArch64::ST2Twov4s_POST,\n    llvm::AArch64::ST2Twov8b_POST,\n    llvm::AArch64::ST2Twov8h_POST,\n    llvm::AArch64::ST2i16,\n    llvm::AArch64::ST2i32,\n    llvm::AArch64::ST2i64,\n    llvm::AArch64::ST2i8,\n    llvm::AArch64::ST3Threev16b_POST,\n    llvm::AArch64::ST3Threev2d_POST,\n    llvm::AArch64::ST3Threev2s_POST,\n    llvm::AArch64::ST3Threev4h_POST,\n    llvm::AArch64::ST3Threev4s_POST,\n    llvm::AArch64::ST3Threev8b_POST,\n    llvm::AArch64::ST3Threev8h_POST,\n    llvm::AArch64::ST3i16,\n    llvm::AArch64::ST3i32,\n    llvm::AArch64::ST3i64,\n    llvm::AArch64::ST3i8,\n    llvm::AArch64::ST4Fourv16b_POST,\n    llvm::AArch64::ST4Fourv2d_POST,\n    llvm::AArch64::ST4Fourv2s_POST,\n    llvm::AArch64::ST4Fourv4h_POST,\n    llvm::AArch64::ST4Fourv4s_POST,\n    llvm::AArch64::ST4Fourv8b_POST,\n    llvm::AArch64::ST4Fourv8h_POST,\n    llvm::AArch64::ST4i16,\n    llvm::AArch64::ST4i32,\n    llvm::AArch64::ST4i64,\n    llvm::AArch64::ST4i8,\n    llvm::AArch64::ST64BV,\n    llvm::AArch64::ST64BV0,\n    llvm::AArch64::STILPW,\n    llvm::AArch64::STILPX,\n    llvm::AArch64::STL1,\n    llvm::AArch64::STLXRB,\n    llvm::AArch64::STLXRH,\n    llvm::AArch64::STLXRW,\n    llvm::AArch64::STLXRX,\n    llvm::AArch64::STRBBpost,\n    llvm::AArch64::STRBpost,\n    llvm::AArch64::STRDpost,\n    llvm::AArch64::STRHHpost,\n    llvm::AArch64::STRHpost,\n    llvm::AArch64::STRQpost,\n    llvm::AArch64::STRSpost,\n    llvm::AArch64::STRWpost,\n    llvm::AArch64::STRXpost,\n    llvm::AArch64::STXRB,\n    llvm::AArch64::STXRH,\n    llvm::AArch64::STXRW,\n    llvm::AArch64::STXRX,\n    llvm::AArch64::SWPAB,\n    llvm::AArch64::SWPAH,\n    llvm::AArch64::SWPALB,\n    llvm::AArch64::SWPALH,\n    llvm::AArch64::SWPALW,\n    llvm::AArch64::SWPALX,\n    llvm::AArch64::SWPAW,\n    llvm::AArch64::SWPAX,\n    llvm::AArch64::SWPB,\n    llvm::AArch64::SWPH,\n    llvm::AArch64::SWPLB,\n    llvm::AArch64::SWPLH,\n    llvm::AArch64::SWPLW,\n    llvm::AArch64::SWPLX,\n    llvm::AArch64::SWPW,\n    llvm::AArch64::SWPX,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_3_SIZE =\n    sizeof(ADDR_REGISTER_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REGISTER_3_FN(const Patch &patch,\n                                                 bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_FN(patch, writeAccess, dest, 2);\n}\n\n// address on the 4th operand\nconstexpr unsigned ADDR_REGISTER_4_TABLE[] = {\n    // casb w0, w1, [x2]\n    // clang-format off\n    llvm::AArch64::CASAB,\n    llvm::AArch64::CASAH,\n    llvm::AArch64::CASALB,\n    llvm::AArch64::CASALH,\n    llvm::AArch64::CASALW,\n    llvm::AArch64::CASALX,\n    llvm::AArch64::CASAW,\n    llvm::AArch64::CASAX,\n    llvm::AArch64::CASB,\n    llvm::AArch64::CASH,\n    llvm::AArch64::CASLB,\n    llvm::AArch64::CASLH,\n    llvm::AArch64::CASLW,\n    llvm::AArch64::CASLX,\n    llvm::AArch64::CASPALW,\n    llvm::AArch64::CASPALX,\n    llvm::AArch64::CASPAW,\n    llvm::AArch64::CASPAX,\n    llvm::AArch64::CASPLW,\n    llvm::AArch64::CASPLX,\n    llvm::AArch64::CASPW,\n    llvm::AArch64::CASPX,\n    llvm::AArch64::CASW,\n    llvm::AArch64::CASX,\n    llvm::AArch64::LD1i16,\n    llvm::AArch64::LD1i32,\n    llvm::AArch64::LD1i64,\n    llvm::AArch64::LD1i8,\n    llvm::AArch64::LD2i16,\n    llvm::AArch64::LD2i32,\n    llvm::AArch64::LD2i64,\n    llvm::AArch64::LD2i8,\n    llvm::AArch64::LD3i16,\n    llvm::AArch64::LD3i32,\n    llvm::AArch64::LD3i64,\n    llvm::AArch64::LD3i8,\n    llvm::AArch64::LD4i16,\n    llvm::AArch64::LD4i32,\n    llvm::AArch64::LD4i64,\n    llvm::AArch64::LD4i8,\n    llvm::AArch64::LDAP1,\n    llvm::AArch64::LDIAPPWpost,\n    llvm::AArch64::LDIAPPXpost,\n    llvm::AArch64::LDPDpost,\n    llvm::AArch64::LDPQpost,\n    llvm::AArch64::LDPSWpost,\n    llvm::AArch64::LDPSpost,\n    llvm::AArch64::LDPWpost,\n    llvm::AArch64::LDPXpost,\n    llvm::AArch64::RCWCAS,\n    llvm::AArch64::RCWCASA,\n    llvm::AArch64::RCWCASAL,\n    llvm::AArch64::RCWCASL,\n    llvm::AArch64::RCWCASP,\n    llvm::AArch64::RCWCASPA,\n    llvm::AArch64::RCWCASPAL,\n    llvm::AArch64::RCWCASPL,\n    llvm::AArch64::RCWSCAS,\n    llvm::AArch64::RCWSCASA,\n    llvm::AArch64::RCWSCASAL,\n    llvm::AArch64::RCWSCASL,\n    llvm::AArch64::RCWSCASP,\n    llvm::AArch64::RCWSCASPA,\n    llvm::AArch64::RCWSCASPAL,\n    llvm::AArch64::RCWSCASPL,\n    llvm::AArch64::RCWSETP,\n    llvm::AArch64::RCWSETPA,\n    llvm::AArch64::RCWSETPAL,\n    llvm::AArch64::RCWSETPL,\n    llvm::AArch64::RCWSETSP,\n    llvm::AArch64::RCWSETSPA,\n    llvm::AArch64::RCWSETSPAL,\n    llvm::AArch64::RCWSETSPL,\n    llvm::AArch64::RCWSWPP,\n    llvm::AArch64::RCWSWPPA,\n    llvm::AArch64::RCWSWPPAL,\n    llvm::AArch64::RCWSWPPL,\n    llvm::AArch64::RCWSWPSP,\n    llvm::AArch64::RCWSWPSPA,\n    llvm::AArch64::RCWSWPSPAL,\n    llvm::AArch64::RCWSWPSPL,\n    llvm::AArch64::ST1i16_POST,\n    llvm::AArch64::ST1i32_POST,\n    llvm::AArch64::ST1i64_POST,\n    llvm::AArch64::ST1i8_POST,\n    llvm::AArch64::ST2i16_POST,\n    llvm::AArch64::ST2i32_POST,\n    llvm::AArch64::ST2i64_POST,\n    llvm::AArch64::ST2i8_POST,\n    llvm::AArch64::ST3i16_POST,\n    llvm::AArch64::ST3i32_POST,\n    llvm::AArch64::ST3i64_POST,\n    llvm::AArch64::ST3i8_POST,\n    llvm::AArch64::ST4i16_POST,\n    llvm::AArch64::ST4i32_POST,\n    llvm::AArch64::ST4i64_POST,\n    llvm::AArch64::ST4i8_POST,\n    llvm::AArch64::STLXPW,\n    llvm::AArch64::STLXPX,\n    llvm::AArch64::STPDpost,\n    llvm::AArch64::STPQpost,\n    llvm::AArch64::STPSpost,\n    llvm::AArch64::STPWpost,\n    llvm::AArch64::STPXpost,\n    llvm::AArch64::STXPW,\n    llvm::AArch64::STXPX,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_4_SIZE =\n    sizeof(ADDR_REGISTER_4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REGISTER_4_FN(const Patch &patch,\n                                                 bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_FN(patch, writeAccess, dest, 3);\n}\n\n// address on the 5th operand\nconstexpr unsigned ADDR_REGISTER_5_TABLE[] = {\n    // ld1\t{ v0.d }[0], [x28], #8\n    // clang-format off\n    llvm::AArch64::LD1i16_POST,\n    llvm::AArch64::LD1i32_POST,\n    llvm::AArch64::LD1i64_POST,\n    llvm::AArch64::LD1i8_POST,\n    llvm::AArch64::LD2i16_POST,\n    llvm::AArch64::LD2i32_POST,\n    llvm::AArch64::LD2i64_POST,\n    llvm::AArch64::LD2i8_POST,\n    llvm::AArch64::LD3i16_POST,\n    llvm::AArch64::LD3i32_POST,\n    llvm::AArch64::LD3i64_POST,\n    llvm::AArch64::LD3i8_POST,\n    llvm::AArch64::LD4i16_POST,\n    llvm::AArch64::LD4i32_POST,\n    llvm::AArch64::LD4i64_POST,\n    llvm::AArch64::LD4i8_POST,\n    llvm::AArch64::LDCLRP,\n    llvm::AArch64::LDCLRPA,\n    llvm::AArch64::LDCLRPAL,\n    llvm::AArch64::LDCLRPL,\n    llvm::AArch64::LDSETP,\n    llvm::AArch64::LDSETPA,\n    llvm::AArch64::LDSETPAL,\n    llvm::AArch64::LDSETPL,\n    llvm::AArch64::RCWCLRP,\n    llvm::AArch64::RCWCLRPA,\n    llvm::AArch64::RCWCLRPAL,\n    llvm::AArch64::RCWCLRPL,\n    llvm::AArch64::RCWCLRSP,\n    llvm::AArch64::RCWCLRSPA,\n    llvm::AArch64::RCWCLRSPAL,\n    llvm::AArch64::RCWCLRSPL,\n    llvm::AArch64::SWPP,\n    llvm::AArch64::SWPPA,\n    llvm::AArch64::SWPPAL,\n    llvm::AArch64::SWPPL,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_5_SIZE =\n    sizeof(ADDR_REGISTER_5_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REGISTER_5_FN(const Patch &patch,\n                                                 bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_FN(patch, writeAccess, dest, 4);\n}\n\n/* Address in a register + immediate\n * =================================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REGISTER_IMM_FN(const Patch &patch,\n                                                   bool writeAccess, Reg dest,\n                                                   unsigned operandOff1,\n                                                   unsigned operandOff2) {\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff1, patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff2, patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  sword imm = inst.getOperand(operandOff2).getImm();\n  return Addc(dest, addrReg, Constant(imm), dest);\n}\n\n// register addr = 2nd operand\n// immediate = 3rd operand\nconstexpr unsigned ADDR_REGISTER_IMM_2_TABLE[] = {\n    // ldtr\tx1, [x17, #0x8]\n    // clang-format off\n    llvm::AArch64::LDAPURBi,\n    llvm::AArch64::LDAPURHi,\n    llvm::AArch64::LDAPURSBWi,\n    llvm::AArch64::LDAPURSBXi,\n    llvm::AArch64::LDAPURSHWi,\n    llvm::AArch64::LDAPURSHXi,\n    llvm::AArch64::LDAPURSWi,\n    llvm::AArch64::LDAPURXi,\n    llvm::AArch64::LDAPURbi,\n    llvm::AArch64::LDAPURdi,\n    llvm::AArch64::LDAPURhi,\n    llvm::AArch64::LDAPURi,\n    llvm::AArch64::LDAPURqi,\n    llvm::AArch64::LDAPURsi,\n    llvm::AArch64::LDTRBi,\n    llvm::AArch64::LDTRHi,\n    llvm::AArch64::LDTRSBWi,\n    llvm::AArch64::LDTRSBXi,\n    llvm::AArch64::LDTRSHWi,\n    llvm::AArch64::LDTRSHXi,\n    llvm::AArch64::LDTRSWi,\n    llvm::AArch64::LDTRWi,\n    llvm::AArch64::LDTRXi,\n    llvm::AArch64::LDURBBi,\n    llvm::AArch64::LDURBi,\n    llvm::AArch64::LDURDi,\n    llvm::AArch64::LDURHHi,\n    llvm::AArch64::LDURHi,\n    llvm::AArch64::LDURQi,\n    llvm::AArch64::LDURSBWi,\n    llvm::AArch64::LDURSBXi,\n    llvm::AArch64::LDURSHWi,\n    llvm::AArch64::LDURSHXi,\n    llvm::AArch64::LDURSWi,\n    llvm::AArch64::LDURSi,\n    llvm::AArch64::LDURWi,\n    llvm::AArch64::LDURXi,\n    llvm::AArch64::STLURBi,\n    llvm::AArch64::STLURHi,\n    llvm::AArch64::STLURWi,\n    llvm::AArch64::STLURXi,\n    llvm::AArch64::STLURbi,\n    llvm::AArch64::STLURdi,\n    llvm::AArch64::STLURhi,\n    llvm::AArch64::STLURqi,\n    llvm::AArch64::STLURsi,\n    llvm::AArch64::STTRBi,\n    llvm::AArch64::STTRHi,\n    llvm::AArch64::STTRWi,\n    llvm::AArch64::STTRXi,\n    llvm::AArch64::STURBBi,\n    llvm::AArch64::STURBi,\n    llvm::AArch64::STURDi,\n    llvm::AArch64::STURHHi,\n    llvm::AArch64::STURHi,\n    llvm::AArch64::STURQi,\n    llvm::AArch64::STURSi,\n    llvm::AArch64::STURWi,\n    llvm::AArch64::STURXi,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_IMM_2_SIZE =\n    sizeof(ADDR_REGISTER_IMM_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_IMM_2_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_IMM_FN(patch, writeAccess, dest, 1, 2);\n}\n\n// register addr = 3rd operand\n// immediate = 4th operand\nconstexpr unsigned ADDR_REGISTER_IMM_3_TABLE[] = {\n    // ldrb\tw1, [x17, #0x8]!\n    // clang-format off\n    llvm::AArch64::LDRBBpre,\n    llvm::AArch64::LDRBpre,\n    llvm::AArch64::LDRDpre,\n    llvm::AArch64::LDRHHpre,\n    llvm::AArch64::LDRHpre,\n    llvm::AArch64::LDRQpre,\n    llvm::AArch64::LDRSBWpre,\n    llvm::AArch64::LDRSBXpre,\n    llvm::AArch64::LDRSHWpre,\n    llvm::AArch64::LDRSHXpre,\n    llvm::AArch64::LDRSWpre,\n    llvm::AArch64::LDRSpre,\n    llvm::AArch64::LDRWpre,\n    llvm::AArch64::LDRXpre,\n    llvm::AArch64::STRBBpre,\n    llvm::AArch64::STRBpre,\n    llvm::AArch64::STRDpre,\n    llvm::AArch64::STRHHpre,\n    llvm::AArch64::STRHpre,\n    llvm::AArch64::STRQpre,\n    llvm::AArch64::STRSpre,\n    llvm::AArch64::STRWpre,\n    llvm::AArch64::STRXpre,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_IMM_3_SIZE =\n    sizeof(ADDR_REGISTER_IMM_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_IMM_3_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_IMM_FN(patch, writeAccess, dest, 2, 3);\n}\n\n/* Address in a register + immediate LSL log2(accessSize)\n * ======================================================\n */\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_IMM_LSL_FN(const Patch &patch, bool writeAccess, Reg dest,\n                         unsigned operandOff1, unsigned operandOff2) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff1, patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff2, patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  sword imm = inst.getOperand(operandOff2).getImm();\n  size_t accessSize;\n  if (writeAccess) {\n    accessSize = getWriteSize(inst, llvmcpu);\n  } else {\n    accessSize = getReadSize(inst, llvmcpu);\n  }\n\n  return Addc(dest, addrReg, Constant(imm * accessSize), dest);\n}\n\n// register addr = 2nd operand\n// immediate = 3rd operand\n// immediate shift = LSL log2(accessSize)\nconstexpr unsigned ADDR_REGISTER_IMM_LSL_2_TABLE[] = {\n    // ldr\th0, [x26, #0x2]\n    // clang-format off\n    llvm::AArch64::LDRBBui,\n    llvm::AArch64::LDRBui,\n    llvm::AArch64::LDRDui,\n    llvm::AArch64::LDRHHui,\n    llvm::AArch64::LDRHui,\n    llvm::AArch64::LDRQui,\n    llvm::AArch64::LDRSBWui,\n    llvm::AArch64::LDRSBXui,\n    llvm::AArch64::LDRSHWui,\n    llvm::AArch64::LDRSHXui,\n    llvm::AArch64::LDRSWui,\n    llvm::AArch64::LDRSui,\n    llvm::AArch64::LDRWui,\n    llvm::AArch64::LDRXui,\n    llvm::AArch64::STRBBui,\n    llvm::AArch64::STRBui,\n    llvm::AArch64::STRDui,\n    llvm::AArch64::STRHHui,\n    llvm::AArch64::STRHui,\n    llvm::AArch64::STRQui,\n    llvm::AArch64::STRSui,\n    llvm::AArch64::STRWui,\n    llvm::AArch64::STRXui,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_IMM_LSL_2_SIZE =\n    sizeof(ADDR_REGISTER_IMM_LSL_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_IMM_LSL_2_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_IMM_LSL_FN(patch, writeAccess, dest, 1, 2);\n}\n\n/* Authanticated Address in a register + immediate LSL log2(accessSize)\n * ====================================================================\n */\n\nRelocatableInst::UniquePtrVec\nADDR_PAC_REGISTER_IMM_LSL_FN(const Patch &patch, bool writeAccess, Reg dest,\n                             unsigned operandOff1, unsigned operandOff2) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff1, patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff2, patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  sword imm = inst.getOperand(operandOff2).getImm();\n  size_t accessSize;\n  if (writeAccess) {\n    accessSize = getWriteSize(inst, llvmcpu);\n  } else {\n    accessSize = getReadSize(inst, llvmcpu);\n  }\n\n  return conv_unique<RelocatableInst>(\n      MovReg::unique(dest, addrReg), Xpacd(dest),\n      // Special usage of ADD\n      // ADD can only support value between -4095 and 4095\n      //      or -4095 and 4095 with a shift of 12\n      // LDRAA can have a immediate between -4096 and 4088\n      // -> [-4095, 4088] is supported with no shift\n      // -> -4096 is supported as -1<<12 (shift = 1)\n      Add(dest, dest, Constant(imm * accessSize)));\n}\n\n// register addr = 2nd operand\n// immediate = 3rd operand\n// immediate shift = LSL log2(accessSize)\nconstexpr unsigned ADDR_PAC_REGISTER_IMM_LSL_2_TABLE[] = {\n    // ldraa\tx0, [x27]\n    // clang-format off\n    llvm::AArch64::LDRAAindexed,\n    llvm::AArch64::LDRABindexed,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_PAC_REGISTER_IMM_LSL_2_SIZE =\n    sizeof(ADDR_PAC_REGISTER_IMM_LSL_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_PAC_REGISTER_IMM_LSL_2_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_PAC_REGISTER_IMM_LSL_FN(patch, writeAccess, dest, 1, 2);\n}\n\n// register addr = 3rd operand\n// immediate = 4th operand\n// immediate shift = LSL log2(accessSize)\nconstexpr unsigned ADDR_PAC_REGISTER_IMM_LSL_3_TABLE[] = {\n    // ldraa\tx0, [x27]!\n    // clang-format off\n    llvm::AArch64::LDRAAwriteback,\n    llvm::AArch64::LDRABwriteback,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_PAC_REGISTER_IMM_LSL_3_SIZE =\n    sizeof(ADDR_PAC_REGISTER_IMM_LSL_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_PAC_REGISTER_IMM_LSL_3_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_PAC_REGISTER_IMM_LSL_FN(patch, writeAccess, dest, 2, 3);\n}\n\n/* Address in a register + immediate LSL log2(accessSize/2)\n * ========================================================\n */\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_IMM_LSL2_FN(const Patch &patch, bool writeAccess, Reg dest,\n                          unsigned operandOff1, unsigned operandOff2) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff1, patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff2, patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  sword imm = inst.getOperand(operandOff2).getImm();\n\n  size_t accessSize;\n  if (writeAccess) {\n    accessSize = getWriteSize(inst, llvmcpu);\n  } else {\n    accessSize = getReadSize(inst, llvmcpu);\n  }\n\n  return Addc(dest, addrReg, Constant(imm * (accessSize / 2)), dest);\n}\n\n// register addr = 3rd operand\n// immediate = 4th operand\n// immediate shift = LSL log2(accessSize/2)\nconstexpr unsigned ADDR_REGISTER_IMM_LSL2_3_TABLE[] = {\n    // ldnp w0, w1, [x17, #4]\n    // clang-format off\n    llvm::AArch64::LDNPDi,\n    llvm::AArch64::LDNPQi,\n    llvm::AArch64::LDNPSi,\n    llvm::AArch64::LDNPWi,\n    llvm::AArch64::LDNPXi,\n    llvm::AArch64::LDPDi,\n    llvm::AArch64::LDPQi,\n    llvm::AArch64::LDPSWi,\n    llvm::AArch64::LDPSi,\n    llvm::AArch64::LDPWi,\n    llvm::AArch64::LDPXi,\n    llvm::AArch64::STNPDi,\n    llvm::AArch64::STNPQi,\n    llvm::AArch64::STNPSi,\n    llvm::AArch64::STNPWi,\n    llvm::AArch64::STNPXi,\n    llvm::AArch64::STPDi,\n    llvm::AArch64::STPQi,\n    llvm::AArch64::STPSi,\n    llvm::AArch64::STPWi,\n    llvm::AArch64::STPXi,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_IMM_LSL2_3_SIZE =\n    sizeof(ADDR_REGISTER_IMM_LSL2_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_IMM_LSL2_3_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_IMM_LSL2_FN(patch, writeAccess, dest, 2, 3);\n}\n\n// register addr = 4th operand\n// immediate = 5th operand\n// immediate shift = LSL log2(accessSize/2)\nconstexpr unsigned ADDR_REGISTER_IMM_LSL2_4_TABLE[] = {\n    // ldp w0, w1, [x17, #4]!\n    // clang-format off\n    llvm::AArch64::LDPDpre,\n    llvm::AArch64::LDPQpre,\n    llvm::AArch64::LDPSWpre,\n    llvm::AArch64::LDPSpre,\n    llvm::AArch64::LDPWpre,\n    llvm::AArch64::LDPXpre,\n    llvm::AArch64::STPDpre,\n    llvm::AArch64::STPQpre,\n    llvm::AArch64::STPSpre,\n    llvm::AArch64::STPWpre,\n    llvm::AArch64::STPXpre,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_IMM_LSL2_4_SIZE =\n    sizeof(ADDR_REGISTER_IMM_LSL2_4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_IMM_LSL2_4_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_IMM_LSL2_FN(patch, writeAccess, dest, 3, 4);\n}\n\n/* Address in a register + FixedImmediate LSL log2(accessSize)\n * =============================================================\n */\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_FIXIMM_LSL_FN(const Patch &patch, bool writeAccess, Reg dest,\n                            unsigned operandOff1, sword imm) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff1, patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n\n  size_t accessSize;\n  if (writeAccess) {\n    accessSize = getWriteSize(inst, llvmcpu);\n  } else {\n    accessSize = getReadSize(inst, llvmcpu);\n  }\n\n  return Addc(dest, addrReg, Constant(imm * (accessSize)), dest);\n}\n\n// register addr = 3rd operand\n// fixedOperand = -1\nconstexpr unsigned ADDR_REGISTER_MIN1_LSL_3_TABLE[] = {\n    // clang-format off\n    llvm::AArch64::STLRWpre,\n    llvm::AArch64::STLRXpre,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_MIN1_LSL_3_SIZE =\n    sizeof(ADDR_REGISTER_MIN1_LSL_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_MIN1_LSL_3_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_FIXIMM_LSL_FN(patch, writeAccess, dest, 2, -1);\n}\n\n// register addr = 4st operand\n// fixedOperand = -1\nconstexpr unsigned ADDR_REGISTER_MIN1_LSL_4_TABLE[] = {\n    // clang-format off\n    llvm::AArch64::STILPWpre,\n    llvm::AArch64::STILPXpre,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_MIN1_LSL_4_SIZE =\n    sizeof(ADDR_REGISTER_MIN1_LSL_4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_MIN1_LSL_4_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_FIXIMM_LSL_FN(patch, writeAccess, dest, 3, -1);\n}\n\n/* regbase address + ( extend register (UXTW|SXTW) SHIFT )\n * =======================================================\n */\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_EXT_FN(const Patch &patch, bool writeAccess, Reg dest,\n                     unsigned operandOff1, unsigned operandOff2,\n                     unsigned operandOff3, unsigned operandOff4) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff1, patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff2, patch);\n  QBDI_REQUIRE_ABORT(operandOff3 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff3, patch);\n  QBDI_REQUIRE_ABORT(operandOff4 < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff4, patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff3).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff4).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  RegLLVM extReg = inst.getOperand(operandOff2).getReg();\n  bool isSigned = inst.getOperand(operandOff3).getImm();\n  bool hasShift = inst.getOperand(operandOff4).getImm();\n\n  size_t shiftValue = 0;\n  if (hasShift) {\n    size_t accessSize;\n    if (writeAccess) {\n      accessSize = getWriteSize(inst, llvmcpu);\n    } else {\n      accessSize = getReadSize(inst, llvmcpu);\n    }\n    switch (accessSize) {\n      case 1:\n        break;\n      case 2:\n        shiftValue = 1;\n        break;\n      case 4:\n        shiftValue = 2;\n        break;\n      case 8:\n        shiftValue = 3;\n        break;\n      case 16:\n        shiftValue = 4;\n        break;\n      default:\n        QBDI_ABORT(\"Unexpected access size {} {}\", accessSize, patch);\n    }\n  }\n  unsigned extRegSize = getRegisterSize(extReg);\n  QBDI_REQUIRE_ABORT(extRegSize == 4 || extRegSize == 8,\n                     \"Unexpected register size {} {}\", extRegSize, patch);\n\n  if (extRegSize == 4) {\n    if (isSigned) {\n      return conv_unique<RelocatableInst>(\n          Addr(dest, addrReg, extReg, SXTW, Constant(shiftValue)));\n    } else {\n      return conv_unique<RelocatableInst>(\n          Addr(dest, addrReg, extReg, UXTW, Constant(shiftValue)));\n    }\n  } else {\n    if (isSigned) {\n      return conv_unique<RelocatableInst>(\n          Addr(dest, addrReg, extReg, SXTX, Constant(shiftValue)));\n    } else {\n      return conv_unique<RelocatableInst>(\n          Addr(dest, addrReg, extReg, UXTX, Constant(shiftValue)));\n    }\n  }\n}\n\n// base addr register = 2nd operand\n// extend register = 3rd operand\n// extend is signed = 4th operand\n// extend has shift = 5th operand\nconstexpr unsigned ADDR_REGISTER_EXT_2_TABLE[] = {\n    // clang-format off\n    llvm::AArch64::LDRBBroW,\n    llvm::AArch64::LDRBBroX,\n    llvm::AArch64::LDRBroW,\n    llvm::AArch64::LDRBroX,\n    llvm::AArch64::LDRDroW,\n    llvm::AArch64::LDRDroX,\n    llvm::AArch64::LDRHHroW,\n    llvm::AArch64::LDRHHroX,\n    llvm::AArch64::LDRHroW,\n    llvm::AArch64::LDRHroX,\n    llvm::AArch64::LDRQroW,\n    llvm::AArch64::LDRQroX,\n    llvm::AArch64::LDRSBWroW,\n    llvm::AArch64::LDRSBWroX,\n    llvm::AArch64::LDRSBXroW,\n    llvm::AArch64::LDRSBXroX,\n    llvm::AArch64::LDRSHWroW,\n    llvm::AArch64::LDRSHWroX,\n    llvm::AArch64::LDRSHXroW,\n    llvm::AArch64::LDRSHXroX,\n    llvm::AArch64::LDRSWroW,\n    llvm::AArch64::LDRSWroX,\n    llvm::AArch64::LDRSroW,\n    llvm::AArch64::LDRSroX,\n    llvm::AArch64::LDRWroW,\n    llvm::AArch64::LDRWroX,\n    llvm::AArch64::LDRXroW,\n    llvm::AArch64::LDRXroX,\n    llvm::AArch64::STRBBroW,\n    llvm::AArch64::STRBBroX,\n    llvm::AArch64::STRBroW,\n    llvm::AArch64::STRBroX,\n    llvm::AArch64::STRDroW,\n    llvm::AArch64::STRDroX,\n    llvm::AArch64::STRHHroW,\n    llvm::AArch64::STRHHroX,\n    llvm::AArch64::STRHroW,\n    llvm::AArch64::STRHroX,\n    llvm::AArch64::STRQroW,\n    llvm::AArch64::STRQroX,\n    llvm::AArch64::STRSroW,\n    llvm::AArch64::STRSroX,\n    llvm::AArch64::STRWroW,\n    llvm::AArch64::STRWroX,\n    llvm::AArch64::STRXroW,\n    llvm::AArch64::STRXroX,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_EXT_2_SIZE =\n    sizeof(ADDR_REGISTER_EXT_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_EXT_2_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_EXT_FN(patch, writeAccess, dest, 1, 2, 3, 4);\n}\n\n/* PC rel + offset * 4\n * ===================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REGISTER_PC_FN(const Patch &patch,\n                                                  bool writeAccess, Reg dest,\n                                                  unsigned operandOff) {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff < inst.getNumOperands(),\n                     \"Invalid operand {} {}\", operandOff, patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  sword offset = inst.getOperand(operandOff).getImm();\n\n  return conv_unique<RelocatableInst>(\n      LoadImm::unique(dest, Constant(address + 4 * offset)));\n}\n\n// offset value = 2nd operand\nconstexpr unsigned ADDR_REGISTER_PC_2_TABLE[] = {\n    // clang-format off\n    llvm::AArch64::LDRDl,\n    llvm::AArch64::LDRQl,\n    llvm::AArch64::LDRSWl,\n    llvm::AArch64::LDRSl,\n    llvm::AArch64::LDRWl,\n    llvm::AArch64::LDRXl,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REGISTER_PC_2_SIZE =\n    sizeof(ADDR_REGISTER_PC_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REGISTER_PC_2_FN(const Patch &patch, bool writeAccess, Reg dest) {\n  return ADDR_REGISTER_PC_FN(patch, writeAccess, dest, 1);\n}\n\nstruct MemoryAccessInfoArray {\n  AddressGenFn *addrFn[15] = {};\n  uint8_t addrArr[llvm::AArch64::INSTRUCTION_LIST_END] = {0};\n\n  constexpr void addData(size_t index, const unsigned insts[],\n                         const size_t instsSize, AddressGenFn *fn) {\n    addrFn[index] = fn;\n    for (size_t i = 0; i < instsSize; i++) {\n      addrArr[insts[i]] = index;\n    }\n  }\n\n  constexpr MemoryAccessInfoArray() {\n    for (size_t i = 0; i < sizeof(addrArr) / sizeof(uint8_t); i++) {\n      addrArr[i] = -1;\n    }\n\n    uint8_t index = 0;\n    addData(index++, ADDR_REGISTER_2_TABLE, ADDR_REGISTER_2_SIZE,\n            ADDR_REGISTER_2_FN);\n    addData(index++, ADDR_REGISTER_3_TABLE, ADDR_REGISTER_3_SIZE,\n            ADDR_REGISTER_3_FN);\n    addData(index++, ADDR_REGISTER_4_TABLE, ADDR_REGISTER_4_SIZE,\n            ADDR_REGISTER_4_FN);\n    addData(index++, ADDR_REGISTER_5_TABLE, ADDR_REGISTER_5_SIZE,\n            ADDR_REGISTER_5_FN);\n    addData(index++, ADDR_REGISTER_IMM_2_TABLE, ADDR_REGISTER_IMM_2_SIZE,\n            ADDR_REGISTER_IMM_2_FN);\n    addData(index++, ADDR_REGISTER_IMM_3_TABLE, ADDR_REGISTER_IMM_3_SIZE,\n            ADDR_REGISTER_IMM_3_FN);\n    addData(index++, ADDR_REGISTER_IMM_LSL_2_TABLE,\n            ADDR_REGISTER_IMM_LSL_2_SIZE, ADDR_REGISTER_IMM_LSL_2_FN);\n    addData(index++, ADDR_PAC_REGISTER_IMM_LSL_2_TABLE,\n            ADDR_PAC_REGISTER_IMM_LSL_2_SIZE, ADDR_PAC_REGISTER_IMM_LSL_2_FN);\n    addData(index++, ADDR_PAC_REGISTER_IMM_LSL_3_TABLE,\n            ADDR_PAC_REGISTER_IMM_LSL_3_SIZE, ADDR_PAC_REGISTER_IMM_LSL_3_FN);\n    addData(index++, ADDR_REGISTER_IMM_LSL2_3_TABLE,\n            ADDR_REGISTER_IMM_LSL2_3_SIZE, ADDR_REGISTER_IMM_LSL2_3_FN);\n    addData(index++, ADDR_REGISTER_IMM_LSL2_4_TABLE,\n            ADDR_REGISTER_IMM_LSL2_4_SIZE, ADDR_REGISTER_IMM_LSL2_4_FN);\n    addData(index++, ADDR_REGISTER_MIN1_LSL_3_TABLE,\n            ADDR_REGISTER_MIN1_LSL_3_SIZE, ADDR_REGISTER_MIN1_LSL_3_FN);\n    addData(index++, ADDR_REGISTER_MIN1_LSL_4_TABLE,\n            ADDR_REGISTER_MIN1_LSL_4_SIZE, ADDR_REGISTER_MIN1_LSL_4_FN);\n    addData(index++, ADDR_REGISTER_EXT_2_TABLE, ADDR_REGISTER_EXT_2_SIZE,\n            ADDR_REGISTER_EXT_2_FN);\n    addData(index++, ADDR_REGISTER_PC_2_TABLE, ADDR_REGISTER_PC_2_SIZE,\n            ADDR_REGISTER_PC_2_FN);\n  }\n};\n\nconstexpr MemoryAccessInfoArray memoryAccessInfo;\n\n#if CHECK_MEMORYACCESS_TABLE\n\nstruct AddressGenerator {\n  const unsigned *insts;\n  size_t nbInsts;\n  AddressGenFn *fn;\n};\n\nint checkTable() {\n  const std::vector<AddressGenerator> addrInfo = {\n      {ADDR_REGISTER_2_TABLE, ADDR_REGISTER_2_SIZE, ADDR_REGISTER_2_FN},\n      {ADDR_REGISTER_3_TABLE, ADDR_REGISTER_3_SIZE, ADDR_REGISTER_3_FN},\n      {ADDR_REGISTER_4_TABLE, ADDR_REGISTER_4_SIZE, ADDR_REGISTER_4_FN},\n      {ADDR_REGISTER_5_TABLE, ADDR_REGISTER_5_SIZE, ADDR_REGISTER_5_FN},\n      {ADDR_REGISTER_IMM_2_TABLE, ADDR_REGISTER_IMM_2_SIZE,\n       ADDR_REGISTER_IMM_2_FN},\n      {ADDR_REGISTER_IMM_3_TABLE, ADDR_REGISTER_IMM_3_SIZE,\n       ADDR_REGISTER_IMM_3_FN},\n      {ADDR_REGISTER_IMM_LSL_2_TABLE, ADDR_REGISTER_IMM_LSL_2_SIZE,\n       ADDR_REGISTER_IMM_LSL_2_FN},\n      {ADDR_PAC_REGISTER_IMM_LSL_2_TABLE, ADDR_PAC_REGISTER_IMM_LSL_2_SIZE,\n       ADDR_PAC_REGISTER_IMM_LSL_2_FN},\n      {ADDR_PAC_REGISTER_IMM_LSL_3_TABLE, ADDR_PAC_REGISTER_IMM_LSL_3_SIZE,\n       ADDR_PAC_REGISTER_IMM_LSL_3_FN},\n      {ADDR_REGISTER_IMM_LSL2_3_TABLE, ADDR_REGISTER_IMM_LSL2_3_SIZE,\n       ADDR_REGISTER_IMM_LSL2_3_FN},\n      {ADDR_REGISTER_IMM_LSL2_4_TABLE, ADDR_REGISTER_IMM_LSL2_4_SIZE,\n       ADDR_REGISTER_IMM_LSL2_4_FN},\n      {ADDR_REGISTER_MIN1_LSL_3_TABLE, ADDR_REGISTER_MIN1_LSL_3_SIZE,\n       ADDR_REGISTER_MIN1_LSL_3_FN},\n      {ADDR_REGISTER_MIN1_LSL_4_TABLE, ADDR_REGISTER_MIN1_LSL_4_SIZE,\n       ADDR_REGISTER_MIN1_LSL_4_FN},\n      {ADDR_REGISTER_EXT_2_TABLE, ADDR_REGISTER_EXT_2_SIZE,\n       ADDR_REGISTER_EXT_2_FN},\n      {ADDR_REGISTER_PC_2_TABLE, ADDR_REGISTER_PC_2_SIZE,\n       ADDR_REGISTER_PC_2_FN}};\n\n  const LLVMCPUs llvmcpus{\"\", {}, Options::NO_OPT};\n  const LLVMCPU &llvmcpu = llvmcpus.getCPU(CPUMode::DEFAULT);\n\n  for (unsigned op = 0; op < llvm::AArch64::INSTRUCTION_LIST_END; op++) {\n    llvm::MCInst inst;\n    inst.setOpcode(op);\n    const char *opcode = llvmcpu.getInstOpcodeName(inst);\n\n    if (getReadSize(inst, llvmcpu) != 0 || getWriteSize(inst, llvmcpu) != 0) {\n      if (memoryAccessInfo.addrArr[op] == ((uint8_t)-1)) {\n        fprintf(stderr,\n                \"[MemoryAccessInfoArray checkTable] \"\n                \"opcode %s doesn't have an associated lambda\\n\",\n                opcode);\n        abort();\n      }\n      unsigned index = 0;\n      for (const auto &e : addrInfo) {\n        for (size_t i = 0; i < e.nbInsts; i++) {\n          unsigned o = e.insts[i];\n          if (o == op && memoryAccessInfo.addrArr[op] != index) {\n            fprintf(\n                stderr,\n                \"[MemoryAccessInfoArray checkTable] \"\n                \"opcode %s associated with lambda %d but expected lambda %d\\n\",\n                opcode, memoryAccessInfo.addrArr[op], index);\n            abort();\n          }\n          if (o == op &&\n              memoryAccessInfo.addrFn[memoryAccessInfo.addrArr[op]] != e.fn) {\n            fprintf(stderr,\n                    \"[MemoryAccessInfoArray checkTable] \"\n                    \"unexpected lambda association for opcode %s : %p != %p\\n\",\n                    opcode,\n                    memoryAccessInfo.addrFn[memoryAccessInfo.addrArr[op]],\n                    e.fn);\n            abort();\n          }\n        }\n        index++;\n      }\n    } else {\n      if (memoryAccessInfo.addrArr[op] != ((uint8_t)-1)) {\n        fprintf(stderr,\n                \"[MemoryAccessInfoArray checkTable] \"\n                \"opcode %s have an associated lambda but doesn't have an \"\n                \"associated memory access size\\n\",\n                opcode);\n        abort();\n      }\n    }\n  }\n  return 0;\n}\n\nint __check_debug = checkTable();\n#endif\n\n} // anonymous namespace\n\nRelocatableInst::UniquePtrVec generateAddressPatch(const Patch &patch,\n                                                   bool writeAccess, Reg dest) {\n\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  if (writeAccess) {\n    QBDI_REQUIRE(getWriteSize(inst, llvmcpu) != 0);\n  } else {\n    QBDI_REQUIRE(getReadSize(inst, llvmcpu) != 0);\n  }\n  const uint8_t index = memoryAccessInfo.addrArr[inst.getOpcode()];\n  QBDI_REQUIRE(index != ((uint8_t)-1));\n\n  return memoryAccessInfo.addrFn[index](patch, writeAccess, dest);\n}\n\n// Generate dynamic PatchGenerator for instruction\n// ===============================================\n\nnamespace {\n\nenum MemoryTag : uint16_t {\n  MEM_READ_ADDRESS_TAG = MEMORY_TAG_BEGIN + 0,\n  MEM_WRITE_ADDRESS_TAG = MEMORY_TAG_BEGIN + 1,\n\n  MEM_READ_VALUE_TAG = MEMORY_TAG_BEGIN + 2,\n  MEM_WRITE_VALUE_TAG = MEMORY_TAG_BEGIN + 3,\n  MEM_VALUE_EXTENDED_TAG = MEMORY_TAG_BEGIN + 4,\n\n  MEM_MOPS_SIZE_TAG = MEMORY_TAG_BEGIN + 5,\n};\n\nconst PatchGenerator::UniquePtrVec &\ngenerateReadInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n  if (llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n        GetReadAddress::unique(Temp(0)),\n        WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)));\n    return r;\n  }\n  switch (getReadSize(patch.metadata.inst, llvmcpu)) {\n    case 1:\n    case 2:\n    case 3:\n    case 4:\n    case 6:\n    case 8: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValue::unique(Temp(0), Temp(0), 0),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_VALUE_TAG)));\n      return r;\n    }\n    case 12: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValue::unique(Temp(1), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)),\n          GetReadValue::unique(Temp(1), Temp(0), 1),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 16: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 24: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetReadValue::unique(Temp(1), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 32: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 48: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 4),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 64: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 4),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetReadValueX2::unique(Temp(1), Temp(2), Temp(0), 6),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    default:\n      QBDI_ABORT(\"Unexpected number of memory Access {} {}\",\n                 getReadSize(patch.metadata.inst, llvmcpu), patch);\n  }\n}\n\nconst PatchGenerator::UniquePtrVec &\ngeneratePreWriteInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n      GetWrittenAddress::unique(Temp(0)),\n      WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)));\n  return r;\n}\n\nconst PatchGenerator::UniquePtrVec &\ngeneratePostWriteInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n  switch (getWriteSize(patch.metadata.inst, llvmcpu)) {\n    case 1:\n    case 2:\n    case 3:\n    case 4:\n    case 6:\n    case 8: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWrittenValue::unique(Temp(0), Temp(0), 0),\n          WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_VALUE_TAG)));\n      return r;\n    }\n    case 12: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWrittenValue::unique(Temp(1), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)),\n          GetWrittenValue::unique(Temp(1), Temp(0), 1),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 16: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 24: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetWrittenValue::unique(Temp(1), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 32: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 48: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 4),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    case 64: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 0),\n          WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 2),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 4),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          GetWrittenValueX2::unique(Temp(1), Temp(2), Temp(0), 6),\n          WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)),\n          WriteTemp::unique(Temp(2), Shadow(MEM_VALUE_EXTENDED_TAG)));\n      return r;\n    }\n    default:\n      QBDI_ABORT(\"Unexpected number of memory Access {} {}\",\n                 getWriteSize(patch.metadata.inst, llvmcpu), patch);\n  }\n}\n\n// MOPS prologue instruction\nconst PatchGenerator::UniquePtrVec &\ngenerateMOPSReadInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n      GetOperand::unique(Temp(0), Operand(1)),\n      WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n      GetOperand::unique(Temp(0), Operand(2)),\n      WriteTemp::unique(Temp(0), Shadow(MEM_MOPS_SIZE_TAG)));\n  return r;\n}\n\nconst PatchGenerator::UniquePtrVec &\ngenerateMOPSWriteInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  switch (patch.metadata.inst.getOpcode()) {\n    case llvm::AArch64::CPYFP:\n    case llvm::AArch64::CPYFPN:\n    case llvm::AArch64::CPYFPRN:\n    case llvm::AArch64::CPYFPRT:\n    case llvm::AArch64::CPYFPRTN:\n    case llvm::AArch64::CPYFPRTRN:\n    case llvm::AArch64::CPYFPRTWN:\n    case llvm::AArch64::CPYFPT:\n    case llvm::AArch64::CPYFPTN:\n    case llvm::AArch64::CPYFPTRN:\n    case llvm::AArch64::CPYFPTWN:\n    case llvm::AArch64::CPYFPWN:\n    case llvm::AArch64::CPYFPWT:\n    case llvm::AArch64::CPYFPWTN:\n    case llvm::AArch64::CPYFPWTRN:\n    case llvm::AArch64::CPYFPWTWN:\n    case llvm::AArch64::CPYP:\n    case llvm::AArch64::CPYPN:\n    case llvm::AArch64::CPYPRN:\n    case llvm::AArch64::CPYPRT:\n    case llvm::AArch64::CPYPRTN:\n    case llvm::AArch64::CPYPRTRN:\n    case llvm::AArch64::CPYPRTWN:\n    case llvm::AArch64::CPYPT:\n    case llvm::AArch64::CPYPTN:\n    case llvm::AArch64::CPYPTRN:\n    case llvm::AArch64::CPYPTWN:\n    case llvm::AArch64::CPYPWN:\n    case llvm::AArch64::CPYPWT:\n    case llvm::AArch64::CPYPWTN:\n    case llvm::AArch64::CPYPWTRN:\n    case llvm::AArch64::CPYPWTWN: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetOperand::unique(Temp(0), Operand(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetOperand::unique(Temp(0), Operand(2)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_MOPS_SIZE_TAG)));\n      return r;\n    }\n    case llvm::AArch64::SETGP:\n    case llvm::AArch64::SETGPN:\n    case llvm::AArch64::SETGPT:\n    case llvm::AArch64::SETGPTN:\n    case llvm::AArch64::SETP:\n    case llvm::AArch64::SETPN:\n    case llvm::AArch64::SETPT:\n    case llvm::AArch64::SETPTN: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetOperand::unique(Temp(0), Operand(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetOperand::unique(Temp(0), Operand(1)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_MOPS_SIZE_TAG)));\n      return r;\n    }\n    default:\n      QBDI_ABORT(\"Unexpected instruction {}\", patch);\n  }\n}\n\n} // anonymous namespace\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessRead() {\n  return conv_unique<InstrRule>(\n      InstrRuleDynamic::unique(And::unique(conv_unique<PatchCondition>(\n                                   DoesReadAccess::unique(),\n                                   Not::unique(IsMOPSReadPrologue::unique()))),\n                               generateReadInstrumentPatch, PREINST, false,\n                               PRIORITY_MEMACCESS_LIMIT + 1,\n                               RelocTagPreInstMemAccess),\n      InstrRuleDynamic::unique(IsMOPSReadPrologue::unique(),\n                               generateMOPSReadInstrumentPatch, PREINST, false,\n                               PRIORITY_MEMACCESS_LIMIT + 1,\n                               RelocTagPreInstMemAccess));\n}\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessWrite() {\n  return conv_unique<InstrRule>(\n      InstrRuleDynamic::unique(And::unique(conv_unique<PatchCondition>(\n                                   DoesWriteAccess::unique(),\n                                   Not::unique(IsMOPSWritePrologue::unique()))),\n                               generatePreWriteInstrumentPatch, PREINST, false,\n                               PRIORITY_MEMACCESS_LIMIT,\n                               RelocTagPreInstMemAccess),\n      InstrRuleDynamic::unique(\n          And::unique(conv_unique<PatchCondition>(\n              DoesWriteAccess::unique(),\n              Not::unique(IsMOPSWritePrologue::unique()),\n              Not::unique(HasOptions::unique(\n                  Options::OPT_DISABLE_MEMORYACCESS_VALUE)))),\n          generatePostWriteInstrumentPatch, POSTINST, false,\n          PRIORITY_MEMACCESS_LIMIT, RelocTagPostInstMemAccess),\n      InstrRuleDynamic::unique(\n          IsMOPSWritePrologue::unique(), generateMOPSWriteInstrumentPatch,\n          PREINST, false, PRIORITY_MEMACCESS_LIMIT, RelocTagPreInstMemAccess));\n}\n\n// Analyse MemoryAccess from Shadow\n// ================================\n\nnamespace {\n\nvoid analyseMemoryAccessAddrValue(const ExecBlock &curExecBlock,\n                                  llvm::ArrayRef<ShadowInfo> &shadows,\n                                  std::vector<MemoryAccess> &dest,\n                                  const LLVMCPU &llvmcpu) {\n  if (shadows.size() < 1) {\n    return;\n  }\n\n  auto access = MemoryAccess();\n  access.flags = MEMORY_NO_FLAGS;\n\n  uint16_t expectValueTag;\n  const llvm::MCInst &inst = curExecBlock.getOriginalMCInst(shadows[0].instID);\n  switch (shadows[0].tag) {\n    default:\n      return;\n    case MEM_READ_ADDRESS_TAG:\n      access.type = MEMORY_READ;\n      access.size = getReadSize(inst, llvmcpu);\n      expectValueTag = MEM_READ_VALUE_TAG;\n      break;\n    case MEM_WRITE_ADDRESS_TAG:\n      access.type = MEMORY_WRITE;\n      access.size = getWriteSize(inst, llvmcpu);\n      expectValueTag = MEM_WRITE_VALUE_TAG;\n      break;\n  }\n\n  access.accessAddress = curExecBlock.getShadow(shadows[0].shadowID);\n  access.instAddress = curExecBlock.getInstAddress(shadows[0].instID);\n\n  if (llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE) and\n      not isMOPSPrologue(inst)) {\n    access.flags |= MEMORY_UNKNOWN_VALUE;\n    access.value = 0;\n    dest.push_back(access);\n    return;\n  }\n\n  size_t index = 0;\n  // search the index of MEM_x_VALUE_TAG. For most instruction, it's the next\n  // shadow.\n  do {\n    index += 1;\n    if (index >= shadows.size()) {\n      QBDI_ERROR(\"Not found shadow tag {:x} for instruction {:x}\",\n                 expectValueTag, access.instAddress);\n      return;\n    }\n    QBDI_REQUIRE_ACTION(shadows[0].instID == shadows[index].instID, return);\n    // special case for MOPS instruction\n    if (shadows[index].tag == MEM_MOPS_SIZE_TAG) {\n      rword size = curExecBlock.getShadow(shadows[index].shadowID);\n      access.value = size;\n      if (size > 0xffff) {\n        access.size = 0xffff;\n        access.flags = MEMORY_UNKNOWN_VALUE | MEMORY_MINIMUM_SIZE;\n      } else {\n        access.size = size;\n        access.flags = MEMORY_UNKNOWN_VALUE;\n      }\n      dest.push_back(access);\n      return;\n    }\n  } while (shadows[index].tag != expectValueTag);\n\n  access.value = curExecBlock.getShadow(shadows[index].shadowID);\n\n  if (access.size < sizeof(rword)) {\n    rword mask = (1ull << (access.size * 8)) - 1;\n    access.value &= mask;\n  }\n\n  size_t extendShadow = 0;\n  size_t remindSize = access.size;\n\n  if (access.size > sizeof(rword)) {\n    extendShadow = (access.size / sizeof(rword));\n    if (access.size % sizeof(rword) == 0 && extendShadow > 0) {\n      --extendShadow;\n    }\n    access.size = sizeof(rword);\n    ++index;\n  }\n\n  dest.push_back(access);\n\n  for (; extendShadow > 0; --extendShadow, ++index) {\n    QBDI_REQUIRE_ACTION(index < shadows.size(), return);\n    QBDI_REQUIRE_ACTION(shadows[0].instID == shadows[index].instID, return);\n    QBDI_REQUIRE_ACTION(shadows[index].tag == MEM_VALUE_EXTENDED_TAG, return);\n\n    access.accessAddress += sizeof(rword);\n    access.value = curExecBlock.getShadow(shadows[index].shadowID);\n    remindSize -= sizeof(rword);\n    if (remindSize < sizeof(rword)) {\n      access.size = remindSize;\n      rword mask = (1ull << (access.size * 8)) - 1;\n      access.value &= mask;\n    }\n    dest.push_back(access);\n  }\n}\n\n} // anonymous namespace\n\nvoid analyseMemoryAccess(const ExecBlock &curExecBlock, uint16_t instID,\n                         bool afterInst, std::vector<MemoryAccess> &dest) {\n\n  llvm::ArrayRef<ShadowInfo> shadows = curExecBlock.getShadowByInst(instID);\n  const LLVMCPU &llvmcpu = curExecBlock.getLLVMCPUByInst(instID);\n  QBDI_DEBUG(\"Got {} shadows for Instruction {:x}\", shadows.size(), instID);\n\n  while (!shadows.empty()) {\n    QBDI_REQUIRE_ACTION(shadows[0].instID == instID, return);\n\n    switch (shadows[0].tag) {\n      default:\n        break;\n      case MEM_READ_ADDRESS_TAG:\n        analyseMemoryAccessAddrValue(curExecBlock, shadows, dest, llvmcpu);\n        break;\n      case MEM_WRITE_ADDRESS_TAG:\n        if (afterInst) {\n          analyseMemoryAccessAddrValue(curExecBlock, shadows, dest, llvmcpu);\n        }\n        break;\n    }\n    shadows = shadows.drop_front();\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/MemoryAccess_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCH_MEMORYACCESS_AARCH64_H\n#define PATCH_MEMORYACCESS_AARCH64_H\n\n#include <memory>\n\n#include \"Patch/MemoryAccess.h\"\n\nnamespace QBDI {\nclass RelocatableInst;\n\n/* Generate the patch to retrive the address of the access from an instruction\n *\n * @param[in] patch          The patch of the instruction that performed the\n * access\n * @param[in] writteneAccess If true, get the address of the written access\n *                           If false, get the address of the read access\n * @param[in] dest           The register to store the address\n *\n * Each instruction of InstInfo that does a memory access must have an\n * associated AddressGenFn\n */\nstd::vector<std::unique_ptr<RelocatableInst>>\ngenerateAddressPatch(const Patch &patch, bool writeAccess, Reg dest);\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/PatchCondition_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Patch/AARCH64/PatchCondition_AARCH64.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/AARCH64/InstInfo_AARCH64.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nbool IsMOPSReadPrologue::test(const Patch &patch,\n                              const LLVMCPU &llvmcpu) const {\n  return isMOPSPrologue(patch.metadata.inst) and\n         unsupportedRead(patch.metadata.inst);\n}\n\nbool IsMOPSWritePrologue::test(const Patch &patch,\n                               const LLVMCPU &llvmcpu) const {\n  return isMOPSPrologue(patch.metadata.inst) and\n         unsupportedWrite(patch.metadata.inst);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/PatchCondition_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHCONDITION_AARCH64_H\n#define PATCHCONDITION_AARCH64_H\n\n#include \"Patch/PatchCondition.h\"\n\nnamespace QBDI {\n\nclass IsMOPSReadPrologue\n    : public AutoClone<PatchCondition, IsMOPSReadPrologue> {\n\npublic:\n  /*! Return true if the instruction has a condition and may be execute as a NOP\n   * depending of the flags\n   */\n  IsMOPSReadPrologue() {};\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass IsMOPSWritePrologue\n    : public AutoClone<PatchCondition, IsMOPSWritePrologue> {\n\npublic:\n  /*! Return true if the instruction has a condition and may be execute as a NOP\n   * depending of the flags\n   */\n  IsMOPSWritePrologue() {};\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/PatchGenerator_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdint.h>\n#include <stdlib.h>\n#include <utility>\n\n#include \"Target/AArch64/AArch64Subtarget.h\"\n#include \"Target/AArch64/MCTargetDesc/AArch64AddressingModes.h\"\n#include \"llvm/MC/MCInst.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/AARCH64/InstInfo_AARCH64.h\"\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/MemoryAccess_AARCH64.h\"\n#include \"Patch/AARCH64/PatchGenerator_AARCH64.h\"\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\ntemplate <typename T>\nRelocatableInst::UniquePtrVec\nPureEval<T>::generate(const Patch &patch, TempManager &temp_manager) const {\n  return this->genReloc(*patch.llvmcpu);\n}\n\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, FullRegisterRestore>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, FullRegisterReset>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, GenBTI>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\n\n// Generic PatchGenerator that must be implemented by each target\n\n// TargetPrologue\n// ==============\n\nRelocatableInst::UniquePtrVec\nTargetPrologue::genReloc(const Patch &patch) const {\n\n  RelocatableInst::UniquePtrVec p = GenBTI().genReloc(*patch.llvmcpu);\n\n  // If the instruction uses X28, restore it\n  if (patch.regUsage[28] != 0) {\n    append(p, LoadReg(Reg(28), Offset(Reg(28))).genReloc(*patch.llvmcpu));\n  }\n\n  return p;\n}\n\n// JmpEpilogue\n// ===========\n\nRelocatableInst::UniquePtrVec\nJmpEpilogue::genReloc(const LLVMCPU &llvmcpu) const {\n\n  return conv_unique<RelocatableInst>(EpilogueAddrRel::unique(branch(0), 0, 0));\n}\n\n// Target Specific PatchGenerator\n\n// SimulateLink\n// ============\n\nRelocatableInst::UniquePtrVec\nSimulateLink::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg tmp = temp_manager.getRegForTemp(temp);\n  return conv_unique<RelocatableInst>(\n      LoadImm::unique(tmp, Constant(patch.metadata.endAddress())),\n      MovReg::unique(Reg(REG_LR), tmp));\n}\n\n// GetPCOffset\n// ===========\n\nRelocatableInst::UniquePtrVec\nGetPCOffset::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  RegLLVM dst(0);\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  switch (type) {\n    case TempConstantType:\n    case TempOperandType:\n      dst = temp_manager.getRegForTemp(temp);\n      break;\n    case OperandOperandType:\n      QBDI_REQUIRE_ABORT(opdst < inst.getNumOperands(), \"Invalid operand {} {}\",\n                         opdst, patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(opdst).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      dst = inst.getOperand(opdst).getReg();\n      break;\n    default:\n      QBDI_ABORT(\"Logical Error {}\", patch);\n  }\n  switch (type) {\n    case TempConstantType: {\n      rword value = patch.metadata.address + cst;\n      return conv_unique<RelocatableInst>(\n          LoadImm::unique(dst, Constant(value)));\n    }\n    case OperandOperandType:\n    case TempOperandType: {\n      QBDI_REQUIRE_ABORT(opsrc < inst.getNumOperands(), \"Invalid operand {} {}\",\n                         opsrc, patch);\n      if (inst.getOperand(opsrc).isImm()) {\n        sword imm = inst.getOperand(opsrc).getImm();\n        switch (inst.getOpcode()) {\n          case llvm::AArch64::ADRP:\n            imm = imm * 0x1000;\n            break;\n          case llvm::AArch64::B:\n          case llvm::AArch64::BL:\n          case llvm::AArch64::Bcc:\n          case llvm::AArch64::BCcc:\n          case llvm::AArch64::CBNZW:\n          case llvm::AArch64::CBNZX:\n          case llvm::AArch64::CBZW:\n          case llvm::AArch64::CBZX:\n          case llvm::AArch64::TBNZW:\n          case llvm::AArch64::TBNZX:\n          case llvm::AArch64::TBZW:\n          case llvm::AArch64::TBZX:\n          case llvm::AArch64::LDRSl:\n          case llvm::AArch64::LDRDl:\n          case llvm::AArch64::LDRQl:\n          case llvm::AArch64::LDRXl:\n          case llvm::AArch64::LDRWl:\n          case llvm::AArch64::LDRSWl:\n            imm = imm * 4;\n            break;\n          default:\n            break;\n        }\n        rword real_addr = patch.metadata.address;\n        if (inst.getOpcode() == llvm::AArch64::ADRP) {\n          // Clear the lower 12-bits\n          real_addr = patch.metadata.address & ~(0xFFFllu);\n        }\n        rword value = real_addr + imm;\n        return conv_unique<RelocatableInst>(\n            LoadImm::unique(dst, Constant(value)));\n      }\n\n      // if (inst.getOperand(opsrc).isReg()) {\n      //   RegLLVM reg = inst.getOperand(opsrc).getReg();\n\n      //   return conv_unique<RelocatableInst>(\n      //       LoadImm::unique(dst, Constant(patch.metadata.address)),\n      //       Add(dst, reg));\n      // }\n      QBDI_ABORT(\"Unsupported Operand type {}\", patch);\n    }\n    default: {\n      QBDI_ABORT(\"Logical Error {}\", patch);\n    }\n  }\n  _QBDI_UNREACHABLE();\n}\n\n// SaveX28IfSet\n// ============\nRelocatableInst::UniquePtrVec\nSaveX28IfSet::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const Patch &p = temp_manager.getPatch();\n  if ((p.regUsage[28] & RegisterSet) != 0) {\n    return SaveReg(Reg(28), Offset(Reg(28))).genReloc(*patch.llvmcpu);\n  }\n\n  return RelocatableInst::UniquePtrVec();\n}\n\n// CondExclusifLoad\n// ================\nRelocatableInst::UniquePtrVec\nCondExclusifLoad::generate(const Patch &patch,\n                           TempManager &temp_manager) const {\n\n  static constexpr int JUMP_OFFSET = 12;\n\n  RelocatableInst::UniquePtrVec p;\n  Reg tmpReg = temp_manager.getRegForTemp(tmp);\n\n  // load enable flag\n  p.push_back(\n      Ldr(tmpReg, Offset(offsetof(Context, gprState.localMonitor.enable))));\n\n  // if monitor in exclusive mode (if cond == 0 => jump)\n  p.push_back(Cbz(tmpReg, Constant(JUMP_OFFSET)));\n\n  // {\n  //  do a exclusive load. reuse the tmp register to store the result\n  p.push_back(\n      Ldr(tmpReg, Offset(offsetof(Context, gprState.localMonitor.addr))));\n  p.push_back(Ldxrb(tmpReg, tmpReg));\n  // }\n\n  return p;\n}\n\n// GetReadAddress\n// ==============\n\nRelocatableInst::UniquePtrVec\nGetReadAddress::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n  return generateAddressPatch(patch, false, tmpRegister);\n}\n\n// GetWrittenAddress\n// =================\n\nRelocatableInst::UniquePtrVec\nGetWrittenAddress::generate(const Patch &patch,\n                            TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n  return generateAddressPatch(patch, true, tmpRegister);\n}\n\n// GetReadValue\n// ============\n\nRelocatableInst::UniquePtrVec\nGetReadValue::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    if (index == 0) {\n      return conv_unique<RelocatableInst>(Mov(tmpRegister, Constant(0)));\n    } else {\n      return {};\n    }\n  }\n\n  Reg addrRegister = temp_manager.getRegForTemp(addr);\n  unsigned readSize = getReadSize(patch.metadata.inst, *patch.llvmcpu);\n  switch (readSize) {\n    case 1:\n      return conv_unique<RelocatableInst>(Ldrb(tmpRegister, addrRegister, 0));\n    case 2:\n      return conv_unique<RelocatableInst>(Ldrh(tmpRegister, addrRegister, 0));\n    case 3: {\n      Reg orRegister = temp_manager.getRegForTemp(Temp(0xffff));\n      return conv_unique<RelocatableInst>(\n          Ldrb(orRegister, addrRegister, 2), Ldrh(tmpRegister, addrRegister, 0),\n          Orrs(tmpRegister, tmpRegister, orRegister, 16));\n    }\n    case 4:\n      return conv_unique<RelocatableInst>(Ldrw(tmpRegister, addrRegister, 0));\n    case 6: {\n      Reg orRegister = temp_manager.getRegForTemp(Temp(0xffff));\n      return conv_unique<RelocatableInst>(\n          Ldrh(orRegister, addrRegister, 4), Ldrw(tmpRegister, addrRegister, 0),\n          Orrs(tmpRegister, tmpRegister, orRegister, 32));\n    }\n    case 8:\n    case 16:\n    case 24:\n    case 32:\n    case 48:\n    case 64:\n      return conv_unique<RelocatableInst>(\n          Ldr(tmpRegister, addrRegister, index * 8));\n    case 12:\n      if (index == 0) {\n        return conv_unique<RelocatableInst>(Ldr(tmpRegister, addrRegister, 0));\n      } else {\n        return conv_unique<RelocatableInst>(Ldrw(tmpRegister, addrRegister, 8));\n      }\n    default:\n      QBDI_ABORT(\"Unexpected Read Size {} {}\", readSize, patch);\n  }\n}\n\n// GetWrittenValue\n// ===============\n\nRelocatableInst::UniquePtrVec\nGetWrittenValue::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    if (index == 0) {\n      return conv_unique<RelocatableInst>(Mov(tmpRegister, Constant(0)));\n    } else {\n      return {};\n    }\n  }\n\n  Reg addrRegister = temp_manager.getRegForTemp(addr);\n  unsigned writtenSize = getWriteSize(patch.metadata.inst, *patch.llvmcpu);\n  switch (writtenSize) {\n    case 1:\n      return conv_unique<RelocatableInst>(Ldrb(tmpRegister, addrRegister, 0));\n    case 2:\n      return conv_unique<RelocatableInst>(Ldrh(tmpRegister, addrRegister, 0));\n    case 3: {\n      Reg orRegister = temp_manager.getRegForTemp(Temp(0xffff));\n      return conv_unique<RelocatableInst>(\n          Ldrh(tmpRegister, addrRegister, 0), Ldrb(orRegister, addrRegister, 2),\n          Orrs(tmpRegister, tmpRegister, orRegister, 16));\n    }\n    case 4:\n      return conv_unique<RelocatableInst>(Ldrw(tmpRegister, addrRegister, 0));\n    case 6: {\n      Reg orRegister = temp_manager.getRegForTemp(Temp(0xffff));\n      return conv_unique<RelocatableInst>(\n          Ldrw(tmpRegister, addrRegister, 0), Ldrh(orRegister, addrRegister, 4),\n          Orrs(tmpRegister, tmpRegister, orRegister, 32));\n    }\n    case 8:\n    case 16:\n    case 24:\n    case 32:\n    case 48:\n    case 64:\n      return conv_unique<RelocatableInst>(\n          Ldr(tmpRegister, addrRegister, index * 8));\n    case 12:\n      if (index == 0) {\n        return conv_unique<RelocatableInst>(Ldr(tmpRegister, addrRegister, 0));\n      } else {\n        return conv_unique<RelocatableInst>(Ldrw(tmpRegister, addrRegister, 8));\n      }\n    default:\n      QBDI_ABORT(\"Unexpected Written Size {} {}\", writtenSize, patch);\n  }\n}\n\n// GetReadValueX2\n// ==============\n\nRelocatableInst::UniquePtrVec\nGetReadValueX2::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n  Reg tmpRegister2 = temp_manager.getRegForTemp(temp2);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    if (index == 0) {\n      return conv_unique<RelocatableInst>(Mov(tmpRegister, Constant(0)),\n                                          Mov(tmpRegister2, Constant(0)));\n    } else {\n      return {};\n    }\n  }\n\n  Reg addrRegister = temp_manager.getRegForTemp(addr);\n  unsigned readSize = getReadSize(patch.metadata.inst, *patch.llvmcpu);\n  switch (readSize) {\n    case 24:\n      if (index == 0) {\n        return conv_unique<RelocatableInst>(\n            Ldp(tmpRegister, tmpRegister2, addrRegister, index * 8));\n      } else {\n        QBDI_ABORT(\"Unsupported index {} for read size 24 {}\", index, patch);\n      }\n    case 16:\n    case 32:\n    case 48:\n    case 64:\n      return conv_unique<RelocatableInst>(\n          Ldp(tmpRegister, tmpRegister2, addrRegister, index * 8));\n    case 1:\n    case 2:\n    case 3:\n    case 4:\n    case 6:\n    case 8:\n    case 12:\n      QBDI_ABORT(\"Unsupported Read Size {} {}\", readSize, patch);\n    default:\n      QBDI_ABORT(\"Unexpected Read Size {} {}\", readSize, patch);\n  }\n}\n\n// GetWrittenValueX2\n// =================\n\nRelocatableInst::UniquePtrVec\nGetWrittenValueX2::generate(const Patch &patch,\n                            TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n  Reg tmpRegister2 = temp_manager.getRegForTemp(temp2);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    if (index == 0) {\n      return conv_unique<RelocatableInst>(Mov(tmpRegister, Constant(0)),\n                                          Mov(tmpRegister2, Constant(0)));\n    } else {\n      return {};\n    }\n  }\n\n  Reg addrRegister = temp_manager.getRegForTemp(addr);\n  unsigned writtenSize = getWriteSize(patch.metadata.inst, *patch.llvmcpu);\n  switch (writtenSize) {\n    case 24:\n      if (index == 0) {\n        return conv_unique<RelocatableInst>(\n            Ldp(tmpRegister, tmpRegister2, addrRegister, index * 8));\n      } else {\n        QBDI_ABORT(\"Unsupported index {} for written size 24 {}\", index, patch);\n      }\n    case 16:\n    case 32:\n    case 48:\n    case 64:\n      return conv_unique<RelocatableInst>(\n          Ldp(tmpRegister, tmpRegister2, addrRegister, index * 8));\n    case 1:\n    case 2:\n    case 3:\n    case 4:\n    case 6:\n    case 8:\n    case 12:\n      QBDI_ABORT(\"Unsupported Writren Size {} {}\", writtenSize, patch);\n    default:\n      QBDI_ABORT(\"Unexpected Written Size {} {}\", writtenSize, patch);\n  }\n}\n\n// FullRegisterRestore\n// ===================\n\nRelocatableInst::UniquePtrVec\nFullRegisterRestore::genReloc(const LLVMCPU &llvmcpu) const {\n\n  if (restoreX28) {\n    return conv_unique<RelocatableInst>(\n        ReadTPIDR(Reg(28)),\n        Str(Reg(28), Offset(offsetof(Context, hostState.tpidr))),\n        Ldr(Reg(28), Offset(Reg(28))), RestoreScratchRegister::unique());\n  } else {\n    return conv_unique<RelocatableInst>(RestoreScratchRegister::unique());\n  }\n}\n\n// FullRegisterReset\n// =================\n\nRelocatableInst::UniquePtrVec\nFullRegisterReset::genReloc(const LLVMCPU &llvmcpu) const {\n  if (restoreX28) {\n    return conv_unique<RelocatableInst>(\n        WriteSRinTPIDR(), ResetScratchRegister::unique(),\n        Str(Reg(28), Offset(Reg(28))), ReadTPIDR(Reg(28)),\n        Str(Reg(28), Offset(offsetof(Context, hostState.scratchRegisterValue))),\n        Ldr(Reg(28), Offset(offsetof(Context, hostState.tpidr))),\n        WriteTPIDR(Reg(28)));\n  } else {\n    return conv_unique<RelocatableInst>(\n        SetScratchRegister::unique(movrr(Reg(28), 0), 2),\n        ResetScratchRegister::unique(),\n        Str(Reg(28),\n            Offset(offsetof(Context, hostState.scratchRegisterValue))));\n  }\n}\n\n// GetAddrAuth\n// ===========\n\nRelocatableInst::UniquePtrVec\nGetAddrAuth::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  RegLLVM dst(0);\n  const llvm::MCInst &inst = patch.metadata.inst;\n  switch (type) {\n    case TempType:\n      dst = temp_manager.getRegForTemp(temp);\n      break;\n    case OperandType:\n      QBDI_REQUIRE_ABORT(op < inst.getNumOperands(), \"Invalid operand {} {}\",\n                         op, patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(op).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      dst = inst.getOperand(op).getReg();\n      break;\n    default:\n      QBDI_ABORT(\"unexpected type {}\", patch);\n  }\n\n  if (bypass) {\n    switch (inst.getOpcode()) {\n      case llvm::AArch64::LDRAAindexed:\n      case llvm::AArch64::LDRABindexed: {\n        QBDI_REQUIRE_ABORT(2 < inst.getNumOperands(), \"Invalid operand {}\",\n                           patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(2).isImm(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM src = inst.getOperand(1).getReg();\n        sword imm = inst.getOperand(2).getImm() * 8;\n        if (src != dst)\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, src),\n                                              Xpaci(dst),\n                                              Add(dst, dst, Constant(imm)));\n        else\n          return conv_unique<RelocatableInst>(Xpaci(dst),\n                                              Add(dst, dst, Constant(imm)));\n      }\n      case llvm::AArch64::LDRAAwriteback:\n      case llvm::AArch64::LDRABwriteback: {\n        QBDI_REQUIRE_ABORT(3 < inst.getNumOperands(), \"Invalid operand {}\",\n                           patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(2).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(3).isImm(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM src = inst.getOperand(2).getReg();\n        sword imm = inst.getOperand(3).getImm() * 8;\n        if (src != dst)\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, src),\n                                              Xpaci(dst),\n                                              Add(dst, dst, Constant(imm)));\n        else\n          return conv_unique<RelocatableInst>(Xpaci(dst),\n                                              Add(dst, dst, Constant(imm)));\n      }\n      case llvm::AArch64::BRAA:\n      case llvm::AArch64::BRAB:\n      case llvm::AArch64::BRAAZ:\n      case llvm::AArch64::BRABZ:\n      case llvm::AArch64::BLRAA:\n      case llvm::AArch64::BLRAB:\n      case llvm::AArch64::BLRAAZ:\n      case llvm::AArch64::BLRABZ: {\n        QBDI_REQUIRE_ABORT(0 < inst.getNumOperands(), \"Invalid operand {}\",\n                           patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM src = inst.getOperand(0).getReg();\n        if (src != dst)\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, src),\n                                              Xpaci(dst));\n        else\n          return conv_unique<RelocatableInst>(Xpaci(dst));\n      }\n      case llvm::AArch64::RETAA:\n      case llvm::AArch64::RETAB:\n        if (dst != Reg(30))\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, Reg(30)),\n                                              Xpaci(dst));\n        else\n          return conv_unique<RelocatableInst>(Xpaci(dst));\n      default:\n        QBDI_ABORT(\"Unexpected opcode {} {}\", inst.getOpcode(), patch);\n    }\n  } else {\n    switch (inst.getOpcode()) {\n      case llvm::AArch64::BRAA:\n      case llvm::AArch64::BLRAA: {\n        QBDI_REQUIRE_ABORT(1 < inst.getNumOperands(), \"Invalid operand {}\",\n                           patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM src = inst.getOperand(0).getReg();\n        RegLLVM ctx = inst.getOperand(1).getReg();\n        if (src != dst)\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, src),\n                                              Autia(dst, ctx));\n        else\n          return conv_unique<RelocatableInst>(Autia(dst, ctx));\n      }\n      case llvm::AArch64::BRAB:\n      case llvm::AArch64::BLRAB: {\n        QBDI_REQUIRE_ABORT(1 < inst.getNumOperands(), \"Invalid operand {}\",\n                           patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM src = inst.getOperand(0).getReg();\n        RegLLVM ctx = inst.getOperand(1).getReg();\n        if (src != dst)\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, src),\n                                              Autib(dst, ctx));\n        else\n          return conv_unique<RelocatableInst>(Autib(dst, ctx));\n      }\n      case llvm::AArch64::BRAAZ:\n      case llvm::AArch64::BLRAAZ: {\n        QBDI_REQUIRE_ABORT(0 < inst.getNumOperands(), \"Invalid operand {}\",\n                           patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM src = inst.getOperand(0).getReg();\n        if (src != dst)\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, src),\n                                              Autiza(dst));\n        else\n          return conv_unique<RelocatableInst>(Autiza(dst));\n      }\n      case llvm::AArch64::BRABZ:\n      case llvm::AArch64::BLRABZ: {\n        QBDI_REQUIRE_ABORT(0 < inst.getNumOperands(), \"Invalid operand {}\",\n                           patch);\n        QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM src = inst.getOperand(0).getReg();\n        if (src != dst)\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, src),\n                                              Autizb(dst));\n        else\n          return conv_unique<RelocatableInst>(Autizb(dst));\n      }\n      case llvm::AArch64::RETAA:\n        if (dst != Reg(30))\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, Reg(30)),\n                                              Autia(dst, Reg(31)));\n        else\n          return conv_unique<RelocatableInst>(Autia(dst, Reg(31)));\n      case llvm::AArch64::RETAB:\n        if (dst != Reg(30))\n          return conv_unique<RelocatableInst>(MovReg::unique(dst, Reg(30)),\n                                              Autib(dst, Reg(31)));\n        else\n          return conv_unique<RelocatableInst>(Autib(dst, Reg(31)));\n\n      default:\n        QBDI_ABORT(\"Unexpected opcode {} {}\", inst.getOpcode(), patch);\n    }\n  }\n}\n\n// GenBTI\n// ======\n\nRelocatableInst::UniquePtrVec GenBTI::genReloc(const LLVMCPU &llvmcpu) const {\n\n  if (llvmcpu.hasOptions(Options::OPT_ENABLE_BTI)) {\n    return conv_unique<RelocatableInst>(BTIj());\n  }\n  return {};\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/PatchGenerator_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHGENERATOR_AARCH64_H\n#define PATCHGENERATOR_AARCH64_H\n\n#include <memory>\n#include <stddef.h>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nclass SimulateLink : public AutoClone<PatchGenerator, SimulateLink> {\n  Temp temp;\n\npublic:\n  /*! Simulate the effects of the link operation performed by BL and BLX\n   * instructions: the address of the next instruction is copied into the LR\n   * register. A temp and a shadow are needed to compute this address.\n   *\n   * @param[in] temp   Any unused temporary, overwritten by this generator.\n   */\n  SimulateLink(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * LDR REG64 temp, MEM64 Shadow(IMM64 (address + 4))\n   * MOV REG64 LR, REG64 temp\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override { return true; }\n};\n\nclass GetPCOffset : public AutoClone<PatchGenerator, GetPCOffset> {\n  Temp temp;\n  Operand opdst;\n  Constant cst;\n  Operand opsrc;\n  enum {\n    TempConstantType,\n    TempOperandType,\n    OperandOperandType,\n  } type;\n\npublic:\n  /*! Interpret a constant as a PC relative offset and copy it in a temporary.\n   * It can be used to obtain the current value of PC by using a constant of 0.\n   *\n   * @param[in] temp     A temporary where the value will be copied.\n   * @param[in] cst      The constant to be used.\n   */\n  GetPCOffset(Temp temp, Constant cst)\n      : temp(temp), opdst(0), cst(cst), opsrc(0), type(TempConstantType) {}\n\n  /*! Interpret an operand as a PC relative offset and copy it in a temporary.\n   * It can be used to obtain jump/call targets or relative memory access\n   * addresses.\n   *\n   * @param[in] temp     A temporary where the value will be copied.\n   * @param[in] op       The operand index (relative to the instruction LLVM\n   * MCInst representation) to be used.\n   */\n  GetPCOffset(Temp temp, Operand op)\n      : temp(temp), opdst(0), cst(0), opsrc(op), type(TempOperandType) {}\n\n  /*! Interpret an operand as a PC relative offset and copy it in a temporary.\n   * It can be used to obtain jump/call targets or relative memory access\n   * addresses.\n   *\n   * @param[in] opdst    The operand where the value will be copied.\n   * @param[in] opsrc    The operand index (relative to the instruction LLVM\n   * MCInst representation) to be used.\n   */\n  GetPCOffset(Operand opdst, Operand opsrc)\n      : temp(0), opdst(opdst), cst(0), opsrc(opsrc), type(OperandOperandType) {}\n\n  /*! Output:\n   *\n   * If cst:\n   * LDR REG32 temp, MEM32 Shadow(IMM32 (cst + address + 8))\n   *\n   * If op is an immediate:\n   * LDR REG32 temp, MEM32 Shadow(IMM32 (op + address + 8))\n   *\n   * If op is a register:\n   * LDR REG32 temp, MEM32 Shadow(IMM32 (address + 8))\n   * ADD REG32 temp, REG32 op\n   *\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass SaveX28IfSet : public AutoClone<PatchGenerator, SaveX28IfSet> {\n\npublic:\n  SaveX28IfSet() {}\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass CondExclusifLoad : public AutoClone<PatchGenerator, CondExclusifLoad> {\n  Temp tmp;\n\npublic:\n  CondExclusifLoad(Temp tmp) : tmp(tmp) {}\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetReadAddress : public AutoClone<PatchGenerator, GetReadAddress> {\n\n  Temp temp;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the address in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory address will be copied.\n   */\n  GetReadAddress(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   * MOV REG64 temp, REG64 addr\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetWrittenAddress : public AutoClone<PatchGenerator, GetWrittenAddress> {\n\n  Temp temp;\n\npublic:\n  /*! Resolve the memory address where the instructions will write its value and\n   * copy the address in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory address will be copied.\n   */\n  GetWrittenAddress(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * if stack access:\n   * MOV REG64 temp, REG64 addr\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetReadValue : public AutoClone<PatchGenerator, GetReadValue> {\n\n  Temp temp;\n  Temp addr;\n  size_t index;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the value in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory value will be copied.\n   * @param[in] addr      A temporary whith the address of the access.\n   * @param[in] index     Index of access to saved when access size > 64\n   */\n  GetReadValue(Temp temp, Temp addr, size_t index)\n      : temp(temp), addr(addr), index(index) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetWrittenValue : public AutoClone<PatchGenerator, GetWrittenValue> {\n\n  Temp temp;\n  Temp addr;\n\n  size_t index;\n\npublic:\n  /*! Resolve the memory address where the instructions has written its value\n   * and copy back the value in a temporary. This PatchGenerator is only\n   * guaranteed to work after the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory value will be copied.\n   * @param[in] addr      A temporary whith the address of the access.\n   * @param[in] index     Index of access to saved when access size > 64\n   */\n  GetWrittenValue(Temp temp, Temp addr, size_t index)\n      : temp(temp), addr(addr), index(index) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetReadValueX2 : public AutoClone<PatchGenerator, GetReadValueX2> {\n\n  Temp temp;\n  Temp temp2;\n  Temp addr;\n  size_t index;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the value in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the first memory value will be\n   * copied.\n   * @param[in] temp2     A temporary where the second memory value will be\n   * copied.\n   * @param[in] addr      A temporary whith the address of the access.\n   * @param[in] index     Index of access to saved when access size > 64\n   */\n  GetReadValueX2(Temp temp, Temp temp2, Temp addr, size_t index)\n      : temp(temp), temp2(temp2), addr(addr), index(index) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetWrittenValueX2 : public AutoClone<PatchGenerator, GetWrittenValueX2> {\n\n  Temp temp;\n  Temp temp2;\n  Temp addr;\n  size_t index;\n\npublic:\n  /*! Resolve the memory address where the instructions has written its value\n   * and copy back the value in a temporary. This PatchGenerator is only\n   * guaranteed to work after the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the first memory value will be\n   * copied.\n   * @param[in] temp2     A temporary where the second memory value will be\n   * copied.\n   * @param[in] addr      A temporary whith the address of the access.\n   * @param[in] index     Index of access to saved when access size > 64\n   */\n  GetWrittenValueX2(Temp temp, Temp temp2, Temp addr, size_t index)\n      : temp(temp), temp2(temp2), addr(addr), index(index) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass FullRegisterRestore\n    : public PureEval<AutoClone<PatchGenerator, FullRegisterRestore>> {\n\n  bool restoreX28;\n\npublic:\n  /*! Restore the ScratchRegister\n   *  if restoreX28, restore also X28 and save TPIDR_EL0 in the datablock\n   *\n   * @param[in] restoreX28   Restore also X28\n   */\n  FullRegisterRestore(bool restoreX28) : restoreX28(restoreX28) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n};\n\nclass FullRegisterReset\n    : public PureEval<AutoClone<PatchGenerator, FullRegisterReset>> {\n\n  bool restoreX28;\n\npublic:\n  /*! Reset the ScratchRegister to the base address\n   *  if restoreX28, restore also X28. TPIDR_EL0 is used to keep all register\n   *  value\n   *\n   * @param[in] restoreX28   Restore also X28\n   */\n  FullRegisterReset(bool restoreX28) : restoreX28(restoreX28) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n};\n\nclass GetAddrAuth : public AutoClone<PatchGenerator, GetAddrAuth> {\n\n  Temp temp;\n  Operand op;\n  enum {\n    TempType,\n    OperandType,\n  } type;\n\n  bool bypass;\n\npublic:\n  /*! Generate a Patch to authenticate a pointer and store it to a temp\n   *\n   * @param[in] temp    the register where to store the value\n   * @param[in] bypass  only drop the authantication tag\n   */\n  GetAddrAuth(Temp temp, bool bypass)\n      : temp(temp), op(0), type(TempType), bypass(bypass) {}\n\n  /*! Generate a Patch to authenticate a pointer and store it to a temp\n   *\n   * @param[in] op      the operand where to store the value\n   * @param[in] bypass  only drop the authantication tag\n   */\n  GetAddrAuth(Operand op, bool bypass)\n      : temp(0), op(op), type(OperandType), bypass(bypass) {}\n\n  /*! Output:\n   *\n   * MOV dest, autia(op(0), op(1))\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GenBTI : public PureEval<AutoClone<PatchGenerator, GenBTI>> {\npublic:\n  /*! Add BTI instruction is OPT_ENABLE_BTI is enabled\n   */\n  GenBTI() {}\n\n  /*! Output:\n   *\n   * BTI\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/PatchRuleAssembly_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"AArch64InstrInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/PatchGenerator_AARCH64.h\"\n#include \"Patch/InstTransform.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/PatchRule.h\"\n#include \"Patch/PatchRuleAssembly.h\"\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"QBDI/Options.h\"\n\nnamespace QBDI {\n\nnamespace {\n\nstd::vector<PatchRule> getDefaultPatchRules(Options opts) {\n  std::vector<PatchRule> rules;\n  bool bypassPauth = ((opts & Options::OPT_BYPASS_PAUTH) != 0);\n\n  /* Rule #0: Restore all register for SVC and BRK\n   *\n   * In AARCH64, all registers are not restore for all instruction.\n   * X28 is only restore if needed and a random register (SR) hold the address\n   * of the datablock. However, for instruction that create an interrupt, we\n   * should restore all registers.\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::SVC),\n                                             OpIs::unique(llvm::AArch64::BRK))),\n      conv_unique<PatchGenerator>(\n          FullRegisterRestore::unique(true),\n          ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n          FullRegisterReset::unique(true)));\n\n  /* Rule #1: Simulate RET and BR\n   * Target:  RET REG64 Xn\n   * Patch:   DataBlock[Offset(PC)] := Xn\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::RET),\n                                             OpIs::unique(llvm::AArch64::BR))),\n      conv_unique<PatchGenerator>(\n          GetOperand::unique(Temp(0), Operand(0)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n          SaveX28IfSet::unique()));\n\n  /* Rule #2: Simulate BLR\n   * Target:  BLR REG64 Xn\n   * Patch:   DataBlock[Offset(RIP)] := Xn\n   *          SimulateLink(Temp(0))\n   */\n  rules.emplace_back(OpIs::unique(llvm::AArch64::BLR),\n                     conv_unique<PatchGenerator>(\n                         GetOperand::unique(Temp(0), Operand(0)),\n                         WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n                         SimulateLink::unique(Temp(0)),\n                         SaveX28IfSet::unique()));\n\n  /* Rule #3: Simulate BL\n   * Target:  BL IMM\n   * Patch:   DataBlock[Offset(RIP)] := PC + Operand(0)\n   *          SimulateLink(Temp(0))\n   */\n  rules.emplace_back(OpIs::unique(llvm::AArch64::BL),\n                     conv_unique<PatchGenerator>(\n                         GetPCOffset::unique(Temp(0), Operand(0)),\n                         WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n                         SimulateLink::unique(Temp(0)),\n                         SaveX28IfSet::unique()));\n\n  /* Rule #4: Simulate B\n   * Target:  B IMM\n   * Patch:   DataBlock[Offset(RIP)] := PC + Operand(0)\n   */\n  rules.emplace_back(OpIs::unique(llvm::AArch64::B),\n                     conv_unique<PatchGenerator>(\n                         GetPCOffset::unique(Temp(0), Operand(0)),\n                         WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n                         SaveX28IfSet::unique()));\n\n  /* Rule #5: Simulate Bcc\n   * Target:  Bcc IMM\n   * Patch:     Temp(0) := PC + Operand(0)\n   *        --- Bcc IMM -> BCC 8\n   *        |   Temp(0) := PC + 4\n   *        --> DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::AArch64::Bcc), OpIs::unique(llvm::AArch64::BCcc))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(1)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(1), Constant(8 / 4)))),\n          GetPCOffset::unique(Temp(0), Constant(4)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n          SaveX28IfSet::unique()));\n\n  /* Rule #6: Simulate ADR and ADRP\n   * Target:  ADR Xn, IMM\n   * Patch:   Xn := PC + Operand(1)\n   *\n   * Target:  ADRP Xn, IMM\n   * Patch:   Xn := (PC & ~0xfff) + (Operand(1) * 0x1000)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::ADRP),\n                                             OpIs::unique(llvm::AArch64::ADR))),\n      conv_unique<PatchGenerator>(GetPCOffset::unique(Operand(0), Operand(1)),\n                                  SaveX28IfSet::unique()));\n\n  /* Rule #7: Simulate TBZ and TBNZ\n   * Target:    TBNZ Xn, #imm, label\n   * Patch:     Temp(0) := PC + Operand(2)\n   *        --- TBNZ Xn, #imm, label -> TBNZ Xn, #imm, 8\n   *        |   Temp(0) := PC + 4\n   *        --> DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::AArch64::TBNZX),\n          OpIs::unique(llvm::AArch64::TBNZW), OpIs::unique(llvm::AArch64::TBZX),\n          OpIs::unique(llvm::AArch64::TBZW))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(2)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(2), Constant(8 / 4)))),\n          GetPCOffset::unique(Temp(0), Constant(4)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n          SaveX28IfSet::unique()));\n\n  /* Rule #8: Simulate CBZ and CBNZ\n   * Target:    TBNZ Xn, #imm, label\n   * Patch:     Temp(0) := PC + Operand(2)\n   *        --- TBNZ Xn, #imm, label -> TBNZ Xn, #imm, 8\n   *        |   Temp(0) := PC + 4\n   *        --> DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::AArch64::CBNZX),\n          OpIs::unique(llvm::AArch64::CBNZW), OpIs::unique(llvm::AArch64::CBZX),\n          OpIs::unique(llvm::AArch64::CBZW))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(1)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(1), Constant(8 / 4)))),\n          GetPCOffset::unique(Temp(0), Constant(4)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n          SaveX28IfSet::unique()));\n\n  /* Rule #9: Simulate load literal\n   * Target:    LDR Xn, label\n   * Patch:     Operand(0) := LDR(PC + Operand(1))\n   */\n  rules.emplace_back(\n      Or::unique(\n          conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::LDRSl),\n                                      OpIs::unique(llvm::AArch64::LDRDl),\n                                      OpIs::unique(llvm::AArch64::LDRQl),\n                                      OpIs::unique(llvm::AArch64::LDRXl),\n                                      OpIs::unique(llvm::AArch64::LDRWl),\n                                      OpIs::unique(llvm::AArch64::LDRSWl))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(1)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              ReplaceOpcode::unique(std::map<unsigned, unsigned>({\n                  {llvm::AArch64::LDRSl, llvm::AArch64::LDRSui},\n                  {llvm::AArch64::LDRDl, llvm::AArch64::LDRDui},\n                  {llvm::AArch64::LDRQl, llvm::AArch64::LDRQui},\n                  {llvm::AArch64::LDRXl, llvm::AArch64::LDRXui},\n                  {llvm::AArch64::LDRWl, llvm::AArch64::LDRWui},\n                  {llvm::AArch64::LDRSWl, llvm::AArch64::LDRSWui},\n              })),\n              AddOperand::unique(Operand(1), Temp(0)),\n              SetOperand::unique(Operand(2), Constant(0)))),\n          SaveX28IfSet::unique()));\n\n  /* Rule #10: Simulate BRAA, BRAB, BRAAZ, BRABZ, RETAA, RETAB\n   * Target:  BRAA Xn, Xm\n   * Patch:   DataBlock[Offset(PC)] := Authia(Xn, Xm)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::AArch64::BRAA), OpIs::unique(llvm::AArch64::BRAB),\n          OpIs::unique(llvm::AArch64::BRAAZ),\n          OpIs::unique(llvm::AArch64::BRABZ),\n          OpIs::unique(llvm::AArch64::RETAA),\n          OpIs::unique(llvm::AArch64::RETAB))),\n      conv_unique<PatchGenerator>(\n          GetAddrAuth::unique(Temp(0), bypassPauth),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n          SaveX28IfSet::unique()));\n\n  /* Rule #11: Simulate BLRAA, BLRAB, BLRAAZ, BLRABZ\n   * Target:  BLRAA Xn, Xm\n   * Patch:   DataBlock[Offset(PC)] := Authia(Xn, Xm)\n   *          SimulateLink(Temp(0))\n   */\n  rules.emplace_back(\n      Or::unique(\n          conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::BLRAA),\n                                      OpIs::unique(llvm::AArch64::BLRAB),\n                                      OpIs::unique(llvm::AArch64::BLRAAZ),\n                                      OpIs::unique(llvm::AArch64::BLRABZ))),\n      conv_unique<PatchGenerator>(\n          GetAddrAuth::unique(Temp(0), bypassPauth),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC))),\n          SimulateLink::unique(Temp(0)), SaveX28IfSet::unique()));\n\n  if (bypassPauth) {\n\n    /* Rule #12: Replace AUTDA, AUTDB, AUTIA, AUTIB\n     * Target:  AUTDA Xn, Xm\n     * Patch:   XPACD Xn\n     */\n    rules.emplace_back(\n        Or::unique(\n            conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::AUTDA),\n                                        OpIs::unique(llvm::AArch64::AUTDB),\n                                        OpIs::unique(llvm::AArch64::AUTIA),\n                                        OpIs::unique(llvm::AArch64::AUTIB))),\n        conv_unique<PatchGenerator>(\n            ModifyInstruction::unique(conv_unique<InstTransform>(\n                ReplaceOpcode::unique(std::map<unsigned, unsigned>({\n                    {llvm::AArch64::AUTDA, llvm::AArch64::XPACD},\n                    {llvm::AArch64::AUTDB, llvm::AArch64::XPACD},\n                    {llvm::AArch64::AUTIA, llvm::AArch64::XPACI},\n                    {llvm::AArch64::AUTIB, llvm::AArch64::XPACI},\n                })),\n                RemoveOperand::unique(Operand(2)))),\n            SaveX28IfSet::unique()));\n\n    /* Rule #13: Replace AUTDZA, AUTDZB, AUTIZA, AUTIZB\n     * Target:  AUTDZA Xn\n     * Patch:   XPACD Xn\n     */\n    rules.emplace_back(\n        Or::unique(\n            conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::AUTDZA),\n                                        OpIs::unique(llvm::AArch64::AUTDZB),\n                                        OpIs::unique(llvm::AArch64::AUTIZA),\n                                        OpIs::unique(llvm::AArch64::AUTIZB))),\n        conv_unique<PatchGenerator>(\n            ModifyInstruction::unique(conv_unique<InstTransform>(\n                ReplaceOpcode::unique(std::map<unsigned, unsigned>({\n                    {llvm::AArch64::AUTDZA, llvm::AArch64::XPACD},\n                    {llvm::AArch64::AUTDZB, llvm::AArch64::XPACD},\n                    {llvm::AArch64::AUTIZA, llvm::AArch64::XPACI},\n                    {llvm::AArch64::AUTIZB, llvm::AArch64::XPACI},\n                })))),\n            SaveX28IfSet::unique()));\n\n    /* Rule #14: Replace AUTIA1716, AUTIB1716\n     * Target:  AUTIA1716\n     * Patch:   XPAID X17\n     */\n    rules.emplace_back(Or::unique(conv_unique<PatchCondition>(\n                           OpIs::unique(llvm::AArch64::AUTIA1716),\n                           OpIs::unique(llvm::AArch64::AUTIB1716))),\n                       conv_unique<PatchGenerator>(\n                           ModifyInstruction::unique(conv_unique<InstTransform>(\n                               SetOpcode::unique(llvm::AArch64::XPACI),\n                               AddOperand::unique(Operand(0), Reg(17)),\n                               AddOperand::unique(Operand(1), Reg(17)))),\n                           SaveX28IfSet::unique()));\n\n    /* Rule #15: Replace AUTIASP, AUTIAZ, AUTIBSP, AUTIBZ\n     * Target:  AUTIASP\n     * Patch:   XPAID X30\n     */\n    rules.emplace_back(Or::unique(conv_unique<PatchCondition>(\n                           OpIs::unique(llvm::AArch64::AUTIASP),\n                           OpIs::unique(llvm::AArch64::AUTIAZ),\n                           OpIs::unique(llvm::AArch64::AUTIBSP),\n                           OpIs::unique(llvm::AArch64::AUTIBZ))),\n                       conv_unique<PatchGenerator>(\n                           ModifyInstruction::unique(conv_unique<InstTransform>(\n                               SetOpcode::unique(llvm::AArch64::XPACI),\n                               AddOperand::unique(Operand(0), Reg(30)),\n                               AddOperand::unique(Operand(1), Reg(30)))),\n                           SaveX28IfSet::unique()));\n\n    /* Rule #16: Replace LDRAA, LDRAB indexed\n     * Target:  LDRAA Xn, [Xm, #imm]\n     * Patch:   LDR Xn, [(unauth Xm + imm), 0]\n     */\n    rules.emplace_back(Or::unique(conv_unique<PatchCondition>(\n                           OpIs::unique(llvm::AArch64::LDRAAindexed),\n                           OpIs::unique(llvm::AArch64::LDRABindexed))),\n                       conv_unique<PatchGenerator>(\n                           GetAddrAuth::unique(Temp(0), true),\n                           ModifyInstruction::unique(conv_unique<InstTransform>(\n                               SetOpcode::unique(llvm::AArch64::LDRXui),\n                               SetOperand::unique(Operand(1), Temp(0)),\n                               SetOperand::unique(Operand(2), Constant(0)))),\n                           SaveX28IfSet::unique()));\n\n    /* Rule #17: Replace LDRAA, LDRAB writeback\n     * Target:  LDRAA Xn, [Xm, #imm]!\n     * Patch:   Xm := (unauth Xm + imm)\n     *          LDR Xn, [Xm, 0]\n     */\n    rules.emplace_back(Or::unique(conv_unique<PatchCondition>(\n                           OpIs::unique(llvm::AArch64::LDRAAwriteback),\n                           OpIs::unique(llvm::AArch64::LDRABwriteback))),\n                       conv_unique<PatchGenerator>(\n                           GetAddrAuth::unique(Operand(2), true),\n                           ModifyInstruction::unique(conv_unique<InstTransform>(\n                               SetOpcode::unique(llvm::AArch64::LDRXui),\n                               RemoveOperand::unique(Operand(0)),\n                               SetOperand::unique(Operand(2), Constant(0)))),\n                           SaveX28IfSet::unique()));\n  }\n\n  if ((opts & Options::OPT_DISABLE_LOCAL_MONITOR) == 0) {\n    /* Rule #12: Clear local monitor state\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::AArch64::CLREX),\n        conv_unique<PatchGenerator>(\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTemp::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            SaveX28IfSet::unique()));\n\n    /* Rule #13: Clear local monitor state on SVC\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::AArch64::SVC),\n        conv_unique<PatchGenerator>(\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            // for SVC, we need to backup the value of Temp(0) after the syscall\n            SaveTemp::unique(Temp(0), true),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTemp::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            SaveX28IfSet::unique()));\n\n    /* Rule #14: exclusive load 1 register\n     */\n    rules.emplace_back(\n        Or::unique(\n            conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::LDXRB),\n                                        OpIs::unique(llvm::AArch64::LDXRH),\n                                        OpIs::unique(llvm::AArch64::LDXRW),\n                                        OpIs::unique(llvm::AArch64::LDXRX),\n                                        OpIs::unique(llvm::AArch64::LDAXRB),\n                                        OpIs::unique(llvm::AArch64::LDAXRH),\n                                        OpIs::unique(llvm::AArch64::LDAXRW),\n                                        OpIs::unique(llvm::AArch64::LDAXRX))),\n        conv_unique<PatchGenerator>(\n            GetConstant::unique(Temp(0), Constant(1)),\n            WriteTemp::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            WriteOperand::unique(\n                Operand(1),\n                Offset(offsetof(Context, gprState.localMonitor.addr))),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            SaveX28IfSet::unique()));\n\n    /* Rule #15: exclusive load 2 register\n     */\n    rules.emplace_back(\n        Or::unique(\n            conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::LDXPW),\n                                        OpIs::unique(llvm::AArch64::LDXPX),\n                                        OpIs::unique(llvm::AArch64::LDAXPW),\n                                        OpIs::unique(llvm::AArch64::LDAXPX))),\n        conv_unique<PatchGenerator>(\n            GetConstant::unique(Temp(0), Constant(1)),\n            WriteTemp::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            WriteOperand::unique(\n                Operand(2),\n                Offset(offsetof(Context, gprState.localMonitor.addr))),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            SaveX28IfSet::unique()));\n\n    /* Rule #16: exclusive store\n     */\n    rules.emplace_back(\n        Or::unique(\n            conv_unique<PatchCondition>(OpIs::unique(llvm::AArch64::STXRB),\n                                        OpIs::unique(llvm::AArch64::STXRH),\n                                        OpIs::unique(llvm::AArch64::STXRW),\n                                        OpIs::unique(llvm::AArch64::STXRX),\n                                        OpIs::unique(llvm::AArch64::STXPW),\n                                        OpIs::unique(llvm::AArch64::STXPX),\n                                        OpIs::unique(llvm::AArch64::STLXRB),\n                                        OpIs::unique(llvm::AArch64::STLXRH),\n                                        OpIs::unique(llvm::AArch64::STLXRW),\n                                        OpIs::unique(llvm::AArch64::STLXRX),\n                                        OpIs::unique(llvm::AArch64::STLXPW),\n                                        OpIs::unique(llvm::AArch64::STLXPX))),\n        conv_unique<PatchGenerator>(\n            CondExclusifLoad::unique(Temp(0)),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTemp::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            SaveX28IfSet::unique()));\n  }\n\n  // rules.push_back(\n  //    PatchRule(\n  //      Or({\n  //        OpIs(llvm::AArch64::PRFMui),\n  //      }),\n  //      {\n  //        DoNotInstrument(),\n  //        //NopGen(),\n  //        ModifyInstruction({})\n  //      })\n  //);\n\n  // Should we ?\n  // rules.push_back(\n  //    PatchRule(\n  //      IsAtomic(),\n  //      {\n  //        DoNotInstrument(),\n  //        ModifyInstruction({})\n  //      }\n  //    )\n  //);\n\n  rules.emplace_back(True::unique(), conv_unique<PatchGenerator>(\n                                         ModifyInstruction::unique(\n                                             InstTransform::UniquePtrVec()),\n                                         SaveX28IfSet::unique()));\n\n  return rules;\n}\n\n} // namespace\n\nPatchRuleAssembly::PatchRuleAssembly(Options opts)\n    : patchRules(getDefaultPatchRules(opts)), options(opts) {}\n\nPatchRuleAssembly::~PatchRuleAssembly() = default;\n\nbool PatchRuleAssembly::changeOptions(Options opts) {\n  const Options needRecreate =\n      Options::OPT_DISABLE_FPR | Options::OPT_DISABLE_OPTIONAL_FPR |\n      Options::OPT_DISABLE_LOCAL_MONITOR | Options::OPT_BYPASS_PAUTH |\n      Options::OPT_DISABLE_MEMORYACCESS_VALUE;\n  if ((opts & needRecreate) != (options & needRecreate)) {\n    patchRules = getDefaultPatchRules(opts);\n    options = opts;\n    return true;\n  }\n  options = opts;\n  return false;\n}\n\nbool PatchRuleAssembly::generate(const llvm::MCInst &inst, rword address,\n                                 uint32_t instSize, const LLVMCPU &llvmcpu,\n                                 std::vector<Patch> &patchList) {\n\n  Patch instPatch{inst, address, instSize, llvmcpu};\n\n  for (uint32_t j = 0; j < patchRules.size(); j++) {\n    if (patchRules[j].canBeApplied(instPatch, llvmcpu)) {\n      QBDI_DEBUG(\"Patch rule {} applied\", j);\n\n      patchRules[j].apply(instPatch, llvmcpu);\n      patchList.push_back(std::move(instPatch));\n      Patch &patch = patchList.back();\n\n      return patch.metadata.modifyPC;\n    }\n  }\n  QBDI_ABORT(\"Not PatchRule found {}\", instPatch);\n}\n\nbool PatchRuleAssembly::earlyEnd(const LLVMCPU &llvmcpu,\n                                 std::vector<Patch> &patchList) {\n  return true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/PatchRuleAssembly_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHRULEASSEMBLY_AARCH64_H\n#define PATCHRULEASSEMBLY_AARCH64_H\n\n#include <stdbool.h>\n\n#include \"Patch/PatchRuleAssemblyBase.h\"\n\nnamespace QBDI {\nclass PatchRule;\n\nclass PatchRuleAssembly final : public PatchRuleAssemblyBase {\n  std::vector<PatchRule> patchRules;\n  Options options;\n\npublic:\n  PatchRuleAssembly(Options opts);\n\n  ~PatchRuleAssembly();\n\n  bool changeOptions(Options opts) override;\n\n  bool generate(const llvm::MCInst &inst, rword address, uint32_t instSize,\n                const LLVMCPU &llvmcpu, std::vector<Patch> &patchList) override;\n\n  bool earlyEnd(const LLVMCPU &llvmcpu, std::vector<Patch> &patchList) override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/Register_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cstdint>\n#include <map>\n#include <stdint.h>\n\n#include \"AArch64InstrInfo.h\"\n\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nconst RegLLVM GPR_ID[] = {\n    llvm::AArch64::X0,  llvm::AArch64::X1,  llvm::AArch64::X2,\n    llvm::AArch64::X3,  llvm::AArch64::X4,  llvm::AArch64::X5,\n    llvm::AArch64::X6,  llvm::AArch64::X7,  llvm::AArch64::X8,\n    llvm::AArch64::X9,  llvm::AArch64::X10, llvm::AArch64::X11,\n    llvm::AArch64::X12, llvm::AArch64::X13, llvm::AArch64::X14,\n    llvm::AArch64::X15, llvm::AArch64::X16, llvm::AArch64::X17,\n    llvm::AArch64::X18, llvm::AArch64::X19, llvm::AArch64::X20,\n    llvm::AArch64::X21, llvm::AArch64::X22, llvm::AArch64::X23,\n    llvm::AArch64::X24, llvm::AArch64::X25, llvm::AArch64::X26,\n    llvm::AArch64::X27, llvm::AArch64::X28, llvm::AArch64::FP,\n    llvm::AArch64::LR,  llvm::AArch64::SP,  llvm::AArch64::NZCV,\n};\n\nconst RegLLVM FLAG_ID[] = {};\n\nconst RegLLVM SEG_ID[] = {\n    // XZR doesn't have an index in GPR or FPR\n    // The instAnalysis will use OPERAND_SEG when XZR is used\n    llvm::AArch64::XZR, llvm::AArch64::WZR};\n\nconst std::map<RegLLVM, int16_t> FPR_ID = {\n    // size 1b\n    {llvm::AArch64::B0, offsetof(FPRState, v0)},\n    {llvm::AArch64::B1, offsetof(FPRState, v1)},\n    {llvm::AArch64::B2, offsetof(FPRState, v2)},\n    {llvm::AArch64::B3, offsetof(FPRState, v3)},\n    {llvm::AArch64::B4, offsetof(FPRState, v4)},\n    {llvm::AArch64::B5, offsetof(FPRState, v5)},\n    {llvm::AArch64::B6, offsetof(FPRState, v6)},\n    {llvm::AArch64::B7, offsetof(FPRState, v7)},\n    {llvm::AArch64::B8, offsetof(FPRState, v8)},\n    {llvm::AArch64::B9, offsetof(FPRState, v9)},\n    {llvm::AArch64::B10, offsetof(FPRState, v10)},\n    {llvm::AArch64::B11, offsetof(FPRState, v11)},\n    {llvm::AArch64::B12, offsetof(FPRState, v12)},\n    {llvm::AArch64::B13, offsetof(FPRState, v13)},\n    {llvm::AArch64::B14, offsetof(FPRState, v14)},\n    {llvm::AArch64::B15, offsetof(FPRState, v15)},\n    {llvm::AArch64::B16, offsetof(FPRState, v16)},\n    {llvm::AArch64::B17, offsetof(FPRState, v17)},\n    {llvm::AArch64::B18, offsetof(FPRState, v18)},\n    {llvm::AArch64::B19, offsetof(FPRState, v19)},\n    {llvm::AArch64::B20, offsetof(FPRState, v20)},\n    {llvm::AArch64::B21, offsetof(FPRState, v21)},\n    {llvm::AArch64::B22, offsetof(FPRState, v22)},\n    {llvm::AArch64::B23, offsetof(FPRState, v23)},\n    {llvm::AArch64::B24, offsetof(FPRState, v24)},\n    {llvm::AArch64::B25, offsetof(FPRState, v25)},\n    {llvm::AArch64::B26, offsetof(FPRState, v26)},\n    {llvm::AArch64::B27, offsetof(FPRState, v27)},\n    {llvm::AArch64::B28, offsetof(FPRState, v28)},\n    {llvm::AArch64::B29, offsetof(FPRState, v29)},\n    {llvm::AArch64::B30, offsetof(FPRState, v30)},\n    {llvm::AArch64::B31, offsetof(FPRState, v31)},\n\n    // size 2b\n    {llvm::AArch64::H0, offsetof(FPRState, v0)},\n    {llvm::AArch64::H1, offsetof(FPRState, v1)},\n    {llvm::AArch64::H2, offsetof(FPRState, v2)},\n    {llvm::AArch64::H3, offsetof(FPRState, v3)},\n    {llvm::AArch64::H4, offsetof(FPRState, v4)},\n    {llvm::AArch64::H5, offsetof(FPRState, v5)},\n    {llvm::AArch64::H6, offsetof(FPRState, v6)},\n    {llvm::AArch64::H7, offsetof(FPRState, v7)},\n    {llvm::AArch64::H8, offsetof(FPRState, v8)},\n    {llvm::AArch64::H9, offsetof(FPRState, v9)},\n    {llvm::AArch64::H10, offsetof(FPRState, v10)},\n    {llvm::AArch64::H11, offsetof(FPRState, v11)},\n    {llvm::AArch64::H12, offsetof(FPRState, v12)},\n    {llvm::AArch64::H13, offsetof(FPRState, v13)},\n    {llvm::AArch64::H14, offsetof(FPRState, v14)},\n    {llvm::AArch64::H15, offsetof(FPRState, v15)},\n    {llvm::AArch64::H16, offsetof(FPRState, v16)},\n    {llvm::AArch64::H17, offsetof(FPRState, v17)},\n    {llvm::AArch64::H18, offsetof(FPRState, v18)},\n    {llvm::AArch64::H19, offsetof(FPRState, v19)},\n    {llvm::AArch64::H20, offsetof(FPRState, v20)},\n    {llvm::AArch64::H21, offsetof(FPRState, v21)},\n    {llvm::AArch64::H22, offsetof(FPRState, v22)},\n    {llvm::AArch64::H23, offsetof(FPRState, v23)},\n    {llvm::AArch64::H24, offsetof(FPRState, v24)},\n    {llvm::AArch64::H25, offsetof(FPRState, v25)},\n    {llvm::AArch64::H26, offsetof(FPRState, v26)},\n    {llvm::AArch64::H27, offsetof(FPRState, v27)},\n    {llvm::AArch64::H28, offsetof(FPRState, v28)},\n    {llvm::AArch64::H29, offsetof(FPRState, v29)},\n    {llvm::AArch64::H30, offsetof(FPRState, v30)},\n    {llvm::AArch64::H31, offsetof(FPRState, v31)},\n\n    // size 4b\n    {llvm::AArch64::S0, offsetof(FPRState, v0)},\n    {llvm::AArch64::S1, offsetof(FPRState, v1)},\n    {llvm::AArch64::S2, offsetof(FPRState, v2)},\n    {llvm::AArch64::S3, offsetof(FPRState, v3)},\n    {llvm::AArch64::S4, offsetof(FPRState, v4)},\n    {llvm::AArch64::S5, offsetof(FPRState, v5)},\n    {llvm::AArch64::S6, offsetof(FPRState, v6)},\n    {llvm::AArch64::S7, offsetof(FPRState, v7)},\n    {llvm::AArch64::S8, offsetof(FPRState, v8)},\n    {llvm::AArch64::S9, offsetof(FPRState, v9)},\n    {llvm::AArch64::S10, offsetof(FPRState, v10)},\n    {llvm::AArch64::S11, offsetof(FPRState, v11)},\n    {llvm::AArch64::S12, offsetof(FPRState, v12)},\n    {llvm::AArch64::S13, offsetof(FPRState, v13)},\n    {llvm::AArch64::S14, offsetof(FPRState, v14)},\n    {llvm::AArch64::S15, offsetof(FPRState, v15)},\n    {llvm::AArch64::S16, offsetof(FPRState, v16)},\n    {llvm::AArch64::S17, offsetof(FPRState, v17)},\n    {llvm::AArch64::S18, offsetof(FPRState, v18)},\n    {llvm::AArch64::S19, offsetof(FPRState, v19)},\n    {llvm::AArch64::S20, offsetof(FPRState, v20)},\n    {llvm::AArch64::S21, offsetof(FPRState, v21)},\n    {llvm::AArch64::S22, offsetof(FPRState, v22)},\n    {llvm::AArch64::S23, offsetof(FPRState, v23)},\n    {llvm::AArch64::S24, offsetof(FPRState, v24)},\n    {llvm::AArch64::S25, offsetof(FPRState, v25)},\n    {llvm::AArch64::S26, offsetof(FPRState, v26)},\n    {llvm::AArch64::S27, offsetof(FPRState, v27)},\n    {llvm::AArch64::S28, offsetof(FPRState, v28)},\n    {llvm::AArch64::S29, offsetof(FPRState, v29)},\n    {llvm::AArch64::S30, offsetof(FPRState, v30)},\n    {llvm::AArch64::S31, offsetof(FPRState, v31)},\n\n    // size 8b\n    {llvm::AArch64::D0, offsetof(FPRState, v0)},\n    {llvm::AArch64::D1, offsetof(FPRState, v1)},\n    {llvm::AArch64::D2, offsetof(FPRState, v2)},\n    {llvm::AArch64::D3, offsetof(FPRState, v3)},\n    {llvm::AArch64::D4, offsetof(FPRState, v4)},\n    {llvm::AArch64::D5, offsetof(FPRState, v5)},\n    {llvm::AArch64::D6, offsetof(FPRState, v6)},\n    {llvm::AArch64::D7, offsetof(FPRState, v7)},\n    {llvm::AArch64::D8, offsetof(FPRState, v8)},\n    {llvm::AArch64::D9, offsetof(FPRState, v9)},\n    {llvm::AArch64::D10, offsetof(FPRState, v10)},\n    {llvm::AArch64::D11, offsetof(FPRState, v11)},\n    {llvm::AArch64::D12, offsetof(FPRState, v12)},\n    {llvm::AArch64::D13, offsetof(FPRState, v13)},\n    {llvm::AArch64::D14, offsetof(FPRState, v14)},\n    {llvm::AArch64::D15, offsetof(FPRState, v15)},\n    {llvm::AArch64::D16, offsetof(FPRState, v16)},\n    {llvm::AArch64::D17, offsetof(FPRState, v17)},\n    {llvm::AArch64::D18, offsetof(FPRState, v18)},\n    {llvm::AArch64::D19, offsetof(FPRState, v19)},\n    {llvm::AArch64::D20, offsetof(FPRState, v20)},\n    {llvm::AArch64::D21, offsetof(FPRState, v21)},\n    {llvm::AArch64::D22, offsetof(FPRState, v22)},\n    {llvm::AArch64::D23, offsetof(FPRState, v23)},\n    {llvm::AArch64::D24, offsetof(FPRState, v24)},\n    {llvm::AArch64::D25, offsetof(FPRState, v25)},\n    {llvm::AArch64::D26, offsetof(FPRState, v26)},\n    {llvm::AArch64::D27, offsetof(FPRState, v27)},\n    {llvm::AArch64::D28, offsetof(FPRState, v28)},\n    {llvm::AArch64::D29, offsetof(FPRState, v29)},\n    {llvm::AArch64::D30, offsetof(FPRState, v30)},\n    {llvm::AArch64::D31, offsetof(FPRState, v31)},\n    {llvm::AArch64::FPCR, offsetof(FPRState, fpcr)},\n    {llvm::AArch64::FPSR, offsetof(FPRState, fpsr)},\n\n    // size 16b\n    {llvm::AArch64::Q0, offsetof(FPRState, v0)},\n    {llvm::AArch64::Q1, offsetof(FPRState, v1)},\n    {llvm::AArch64::Q2, offsetof(FPRState, v2)},\n    {llvm::AArch64::Q3, offsetof(FPRState, v3)},\n    {llvm::AArch64::Q4, offsetof(FPRState, v4)},\n    {llvm::AArch64::Q5, offsetof(FPRState, v5)},\n    {llvm::AArch64::Q6, offsetof(FPRState, v6)},\n    {llvm::AArch64::Q7, offsetof(FPRState, v7)},\n    {llvm::AArch64::Q8, offsetof(FPRState, v8)},\n    {llvm::AArch64::Q9, offsetof(FPRState, v9)},\n    {llvm::AArch64::Q10, offsetof(FPRState, v10)},\n    {llvm::AArch64::Q11, offsetof(FPRState, v11)},\n    {llvm::AArch64::Q12, offsetof(FPRState, v12)},\n    {llvm::AArch64::Q13, offsetof(FPRState, v13)},\n    {llvm::AArch64::Q14, offsetof(FPRState, v14)},\n    {llvm::AArch64::Q15, offsetof(FPRState, v15)},\n    {llvm::AArch64::Q16, offsetof(FPRState, v16)},\n    {llvm::AArch64::Q17, offsetof(FPRState, v17)},\n    {llvm::AArch64::Q18, offsetof(FPRState, v18)},\n    {llvm::AArch64::Q19, offsetof(FPRState, v19)},\n    {llvm::AArch64::Q20, offsetof(FPRState, v20)},\n    {llvm::AArch64::Q21, offsetof(FPRState, v21)},\n    {llvm::AArch64::Q22, offsetof(FPRState, v22)},\n    {llvm::AArch64::Q23, offsetof(FPRState, v23)},\n    {llvm::AArch64::Q24, offsetof(FPRState, v24)},\n    {llvm::AArch64::Q25, offsetof(FPRState, v25)},\n    {llvm::AArch64::Q26, offsetof(FPRState, v26)},\n    {llvm::AArch64::Q27, offsetof(FPRState, v27)},\n    {llvm::AArch64::Q28, offsetof(FPRState, v28)},\n    {llvm::AArch64::Q29, offsetof(FPRState, v29)},\n    {llvm::AArch64::Q30, offsetof(FPRState, v30)},\n    {llvm::AArch64::Q31, offsetof(FPRState, v31)},\n};\n\nconst unsigned int size_GPR_ID = sizeof(GPR_ID) / sizeof(RegLLVM);\nconst unsigned int size_FLAG_ID = sizeof(FLAG_ID) / sizeof(RegLLVM);\nconst unsigned int size_SEG_ID = sizeof(SEG_ID) / sizeof(RegLLVM);\n\nnamespace {\n\nconstexpr uint16_t REGISTER_1BYTE[] = {\n    llvm::AArch64::B0,  llvm::AArch64::B1,  llvm::AArch64::B2,\n    llvm::AArch64::B3,  llvm::AArch64::B4,  llvm::AArch64::B5,\n    llvm::AArch64::B6,  llvm::AArch64::B7,  llvm::AArch64::B8,\n    llvm::AArch64::B9,  llvm::AArch64::B10, llvm::AArch64::B11,\n    llvm::AArch64::B12, llvm::AArch64::B13, llvm::AArch64::B14,\n    llvm::AArch64::B15, llvm::AArch64::B16, llvm::AArch64::B17,\n    llvm::AArch64::B18, llvm::AArch64::B19, llvm::AArch64::B20,\n    llvm::AArch64::B21, llvm::AArch64::B22, llvm::AArch64::B23,\n    llvm::AArch64::B24, llvm::AArch64::B25, llvm::AArch64::B26,\n    llvm::AArch64::B27, llvm::AArch64::B28, llvm::AArch64::B29,\n    llvm::AArch64::B30, llvm::AArch64::B31,\n};\n\nconstexpr size_t REGISTER_1BYTE_SIZE =\n    sizeof(REGISTER_1BYTE) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_2BYTES[] = {\n    llvm::AArch64::H0,  llvm::AArch64::H1,  llvm::AArch64::H2,\n    llvm::AArch64::H3,  llvm::AArch64::H4,  llvm::AArch64::H5,\n    llvm::AArch64::H6,  llvm::AArch64::H7,  llvm::AArch64::H8,\n    llvm::AArch64::H9,  llvm::AArch64::H10, llvm::AArch64::H11,\n    llvm::AArch64::H12, llvm::AArch64::H13, llvm::AArch64::H14,\n    llvm::AArch64::H15, llvm::AArch64::H16, llvm::AArch64::H17,\n    llvm::AArch64::H18, llvm::AArch64::H19, llvm::AArch64::H20,\n    llvm::AArch64::H21, llvm::AArch64::H22, llvm::AArch64::H23,\n    llvm::AArch64::H24, llvm::AArch64::H25, llvm::AArch64::H26,\n    llvm::AArch64::H27, llvm::AArch64::H28, llvm::AArch64::H29,\n    llvm::AArch64::H30, llvm::AArch64::H31,\n};\n\nconstexpr size_t REGISTER_2BYTES_SIZE =\n    sizeof(REGISTER_2BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_4BYTES[] = {\n    llvm::AArch64::W0,  llvm::AArch64::W1,  llvm::AArch64::W2,\n    llvm::AArch64::W3,  llvm::AArch64::W4,  llvm::AArch64::W5,\n    llvm::AArch64::W6,  llvm::AArch64::W7,  llvm::AArch64::W8,\n    llvm::AArch64::W9,  llvm::AArch64::W10, llvm::AArch64::W11,\n    llvm::AArch64::W12, llvm::AArch64::W13, llvm::AArch64::W14,\n    llvm::AArch64::W15, llvm::AArch64::W16, llvm::AArch64::W17,\n    llvm::AArch64::W18, llvm::AArch64::W19, llvm::AArch64::W20,\n    llvm::AArch64::W21, llvm::AArch64::W22, llvm::AArch64::W23,\n    llvm::AArch64::W24, llvm::AArch64::W25, llvm::AArch64::W26,\n    llvm::AArch64::W27, llvm::AArch64::W28, llvm::AArch64::W29,\n    llvm::AArch64::W30, llvm::AArch64::WSP, llvm::AArch64::WZR,\n\n    llvm::AArch64::S0,  llvm::AArch64::S1,  llvm::AArch64::S2,\n    llvm::AArch64::S3,  llvm::AArch64::S4,  llvm::AArch64::S5,\n    llvm::AArch64::S6,  llvm::AArch64::S7,  llvm::AArch64::S8,\n    llvm::AArch64::S9,  llvm::AArch64::S10, llvm::AArch64::S11,\n    llvm::AArch64::S12, llvm::AArch64::S13, llvm::AArch64::S14,\n    llvm::AArch64::S15, llvm::AArch64::S16, llvm::AArch64::S17,\n    llvm::AArch64::S18, llvm::AArch64::S19, llvm::AArch64::S20,\n    llvm::AArch64::S21, llvm::AArch64::S22, llvm::AArch64::S23,\n    llvm::AArch64::S24, llvm::AArch64::S25, llvm::AArch64::S26,\n    llvm::AArch64::S27, llvm::AArch64::S28, llvm::AArch64::S29,\n    llvm::AArch64::S30, llvm::AArch64::S31,\n};\n\nconstexpr size_t REGISTER_4BYTES_SIZE =\n    sizeof(REGISTER_4BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_4BYTES_P2[] = {\n    llvm::AArch64::W0_W1,   llvm::AArch64::W2_W3,   llvm::AArch64::W4_W5,\n    llvm::AArch64::W6_W7,   llvm::AArch64::W8_W9,   llvm::AArch64::W10_W11,\n    llvm::AArch64::W12_W13, llvm::AArch64::W14_W15, llvm::AArch64::W16_W17,\n    llvm::AArch64::W18_W19, llvm::AArch64::W20_W21, llvm::AArch64::W22_W23,\n    llvm::AArch64::W24_W25, llvm::AArch64::W26_W27, llvm::AArch64::W28_W29,\n    llvm::AArch64::W30_WZR,\n};\n\nconstexpr size_t REGISTER_4BYTES_P2_SIZE =\n    sizeof(REGISTER_4BYTES_P2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES[] = {\n    llvm::AArch64::X0,   llvm::AArch64::X1,  llvm::AArch64::X2,\n    llvm::AArch64::X3,   llvm::AArch64::X4,  llvm::AArch64::X5,\n    llvm::AArch64::X6,   llvm::AArch64::X7,  llvm::AArch64::X8,\n    llvm::AArch64::X9,   llvm::AArch64::X10, llvm::AArch64::X11,\n    llvm::AArch64::X12,  llvm::AArch64::X13, llvm::AArch64::X14,\n    llvm::AArch64::X15,  llvm::AArch64::X16, llvm::AArch64::X17,\n    llvm::AArch64::X18,  llvm::AArch64::X19, llvm::AArch64::X20,\n    llvm::AArch64::X21,  llvm::AArch64::X22, llvm::AArch64::X23,\n    llvm::AArch64::X24,  llvm::AArch64::X25, llvm::AArch64::X26,\n    llvm::AArch64::X27,  llvm::AArch64::X28, llvm::AArch64::FP,\n    llvm::AArch64::LR,   llvm::AArch64::SP,  llvm::AArch64::XZR,\n\n    llvm::AArch64::D0,   llvm::AArch64::D1,  llvm::AArch64::D2,\n    llvm::AArch64::D3,   llvm::AArch64::D4,  llvm::AArch64::D5,\n    llvm::AArch64::D6,   llvm::AArch64::D7,  llvm::AArch64::D8,\n    llvm::AArch64::D9,   llvm::AArch64::D10, llvm::AArch64::D11,\n    llvm::AArch64::D12,  llvm::AArch64::D13, llvm::AArch64::D14,\n    llvm::AArch64::D15,  llvm::AArch64::D16, llvm::AArch64::D17,\n    llvm::AArch64::D18,  llvm::AArch64::D19, llvm::AArch64::D20,\n    llvm::AArch64::D21,  llvm::AArch64::D22, llvm::AArch64::D23,\n    llvm::AArch64::D24,  llvm::AArch64::D25, llvm::AArch64::D26,\n    llvm::AArch64::D27,  llvm::AArch64::D28, llvm::AArch64::D29,\n    llvm::AArch64::D30,  llvm::AArch64::D31, llvm::AArch64::FPCR,\n    llvm::AArch64::FPSR,\n};\n\nconstexpr size_t REGISTER_8BYTES_SIZE =\n    sizeof(REGISTER_8BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P2[] = {\n    llvm::AArch64::D0_D1,   llvm::AArch64::D1_D2,   llvm::AArch64::D2_D3,\n    llvm::AArch64::D3_D4,   llvm::AArch64::D4_D5,   llvm::AArch64::D5_D6,\n    llvm::AArch64::D6_D7,   llvm::AArch64::D7_D8,   llvm::AArch64::D8_D9,\n    llvm::AArch64::D9_D10,  llvm::AArch64::D10_D11, llvm::AArch64::D11_D12,\n    llvm::AArch64::D12_D13, llvm::AArch64::D13_D14, llvm::AArch64::D14_D15,\n    llvm::AArch64::D15_D16, llvm::AArch64::D16_D17, llvm::AArch64::D17_D18,\n    llvm::AArch64::D18_D19, llvm::AArch64::D19_D20, llvm::AArch64::D20_D21,\n    llvm::AArch64::D21_D22, llvm::AArch64::D22_D23, llvm::AArch64::D23_D24,\n    llvm::AArch64::D24_D25, llvm::AArch64::D25_D26, llvm::AArch64::D26_D27,\n    llvm::AArch64::D27_D28, llvm::AArch64::D28_D29, llvm::AArch64::D29_D30,\n    llvm::AArch64::D30_D31, llvm::AArch64::D31_D0,\n\n    llvm::AArch64::X0_X1,   llvm::AArch64::X2_X3,   llvm::AArch64::X4_X5,\n    llvm::AArch64::X6_X7,   llvm::AArch64::X8_X9,   llvm::AArch64::X10_X11,\n    llvm::AArch64::X12_X13, llvm::AArch64::X14_X15, llvm::AArch64::X16_X17,\n    llvm::AArch64::X18_X19, llvm::AArch64::X20_X21, llvm::AArch64::X22_X23,\n    llvm::AArch64::X24_X25, llvm::AArch64::X26_X27, llvm::AArch64::X28_FP,\n    llvm::AArch64::LR_XZR,\n};\n\nconstexpr size_t REGISTER_8BYTES_P2_SIZE =\n    sizeof(REGISTER_8BYTES_P2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P3[] = {\n    llvm::AArch64::D0_D1_D2,    llvm::AArch64::D1_D2_D3,\n    llvm::AArch64::D2_D3_D4,    llvm::AArch64::D3_D4_D5,\n    llvm::AArch64::D4_D5_D6,    llvm::AArch64::D5_D6_D7,\n    llvm::AArch64::D6_D7_D8,    llvm::AArch64::D7_D8_D9,\n    llvm::AArch64::D8_D9_D10,   llvm::AArch64::D9_D10_D11,\n    llvm::AArch64::D10_D11_D12, llvm::AArch64::D11_D12_D13,\n    llvm::AArch64::D12_D13_D14, llvm::AArch64::D13_D14_D15,\n    llvm::AArch64::D14_D15_D16, llvm::AArch64::D15_D16_D17,\n    llvm::AArch64::D16_D17_D18, llvm::AArch64::D17_D18_D19,\n    llvm::AArch64::D18_D19_D20, llvm::AArch64::D19_D20_D21,\n    llvm::AArch64::D20_D21_D22, llvm::AArch64::D21_D22_D23,\n    llvm::AArch64::D22_D23_D24, llvm::AArch64::D23_D24_D25,\n    llvm::AArch64::D24_D25_D26, llvm::AArch64::D25_D26_D27,\n    llvm::AArch64::D26_D27_D28, llvm::AArch64::D27_D28_D29,\n    llvm::AArch64::D28_D29_D30, llvm::AArch64::D29_D30_D31,\n    llvm::AArch64::D30_D31_D0,  llvm::AArch64::D31_D0_D1,\n};\n\nconstexpr size_t REGISTER_8BYTES_P3_SIZE =\n    sizeof(REGISTER_8BYTES_P3) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P4[] = {\n    llvm::AArch64::D0_D1_D2_D3,     llvm::AArch64::D1_D2_D3_D4,\n    llvm::AArch64::D2_D3_D4_D5,     llvm::AArch64::D3_D4_D5_D6,\n    llvm::AArch64::D4_D5_D6_D7,     llvm::AArch64::D5_D6_D7_D8,\n    llvm::AArch64::D6_D7_D8_D9,     llvm::AArch64::D7_D8_D9_D10,\n    llvm::AArch64::D8_D9_D10_D11,   llvm::AArch64::D9_D10_D11_D12,\n    llvm::AArch64::D10_D11_D12_D13, llvm::AArch64::D11_D12_D13_D14,\n    llvm::AArch64::D12_D13_D14_D15, llvm::AArch64::D13_D14_D15_D16,\n    llvm::AArch64::D14_D15_D16_D17, llvm::AArch64::D15_D16_D17_D18,\n    llvm::AArch64::D16_D17_D18_D19, llvm::AArch64::D17_D18_D19_D20,\n    llvm::AArch64::D18_D19_D20_D21, llvm::AArch64::D19_D20_D21_D22,\n    llvm::AArch64::D20_D21_D22_D23, llvm::AArch64::D21_D22_D23_D24,\n    llvm::AArch64::D22_D23_D24_D25, llvm::AArch64::D23_D24_D25_D26,\n    llvm::AArch64::D24_D25_D26_D27, llvm::AArch64::D25_D26_D27_D28,\n    llvm::AArch64::D26_D27_D28_D29, llvm::AArch64::D27_D28_D29_D30,\n    llvm::AArch64::D28_D29_D30_D31, llvm::AArch64::D29_D30_D31_D0,\n    llvm::AArch64::D30_D31_D0_D1,   llvm::AArch64::D31_D0_D1_D2,\n};\n\nconstexpr size_t REGISTER_8BYTES_P4_SIZE =\n    sizeof(REGISTER_8BYTES_P4) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P8[] = {\n    llvm::AArch64::X22_X23_X24_X25_X26_X27_X28_FP,\n    llvm::AArch64::X0_X1_X2_X3_X4_X5_X6_X7,\n    llvm::AArch64::X2_X3_X4_X5_X6_X7_X8_X9,\n    llvm::AArch64::X4_X5_X6_X7_X8_X9_X10_X11,\n    llvm::AArch64::X6_X7_X8_X9_X10_X11_X12_X13,\n    llvm::AArch64::X8_X9_X10_X11_X12_X13_X14_X15,\n    llvm::AArch64::X10_X11_X12_X13_X14_X15_X16_X17,\n    llvm::AArch64::X12_X13_X14_X15_X16_X17_X18_X19,\n    llvm::AArch64::X14_X15_X16_X17_X18_X19_X20_X21,\n    llvm::AArch64::X16_X17_X18_X19_X20_X21_X22_X23,\n    llvm::AArch64::X18_X19_X20_X21_X22_X23_X24_X25,\n    llvm::AArch64::X20_X21_X22_X23_X24_X25_X26_X27,\n};\n\nconstexpr size_t REGISTER_8BYTES_P8_SIZE =\n    sizeof(REGISTER_8BYTES_P8) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES[] = {\n    llvm::AArch64::Q0,  llvm::AArch64::Q1,  llvm::AArch64::Q2,\n    llvm::AArch64::Q3,  llvm::AArch64::Q4,  llvm::AArch64::Q5,\n    llvm::AArch64::Q6,  llvm::AArch64::Q7,  llvm::AArch64::Q8,\n    llvm::AArch64::Q9,  llvm::AArch64::Q10, llvm::AArch64::Q11,\n    llvm::AArch64::Q12, llvm::AArch64::Q13, llvm::AArch64::Q14,\n    llvm::AArch64::Q15, llvm::AArch64::Q16, llvm::AArch64::Q17,\n    llvm::AArch64::Q18, llvm::AArch64::Q19, llvm::AArch64::Q20,\n    llvm::AArch64::Q21, llvm::AArch64::Q22, llvm::AArch64::Q23,\n    llvm::AArch64::Q24, llvm::AArch64::Q25, llvm::AArch64::Q26,\n    llvm::AArch64::Q27, llvm::AArch64::Q28, llvm::AArch64::Q29,\n    llvm::AArch64::Q30, llvm::AArch64::Q31,\n};\n\nconstexpr size_t REGISTER_16BYTES_SIZE =\n    sizeof(REGISTER_16BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES_P2[] = {\n    llvm::AArch64::Q0_Q1,   llvm::AArch64::Q1_Q2,   llvm::AArch64::Q2_Q3,\n    llvm::AArch64::Q3_Q4,   llvm::AArch64::Q4_Q5,   llvm::AArch64::Q5_Q6,\n    llvm::AArch64::Q6_Q7,   llvm::AArch64::Q7_Q8,   llvm::AArch64::Q8_Q9,\n    llvm::AArch64::Q9_Q10,  llvm::AArch64::Q10_Q11, llvm::AArch64::Q11_Q12,\n    llvm::AArch64::Q12_Q13, llvm::AArch64::Q13_Q14, llvm::AArch64::Q14_Q15,\n    llvm::AArch64::Q15_Q16, llvm::AArch64::Q16_Q17, llvm::AArch64::Q17_Q18,\n    llvm::AArch64::Q18_Q19, llvm::AArch64::Q19_Q20, llvm::AArch64::Q20_Q21,\n    llvm::AArch64::Q21_Q22, llvm::AArch64::Q22_Q23, llvm::AArch64::Q23_Q24,\n    llvm::AArch64::Q24_Q25, llvm::AArch64::Q25_Q26, llvm::AArch64::Q26_Q27,\n    llvm::AArch64::Q27_Q28, llvm::AArch64::Q28_Q29, llvm::AArch64::Q29_Q30,\n    llvm::AArch64::Q30_Q31, llvm::AArch64::Q31_Q0,\n};\n\nconstexpr size_t REGISTER_16BYTES_P2_SIZE =\n    sizeof(REGISTER_16BYTES_P2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES_P3[] = {\n    llvm::AArch64::Q0_Q1_Q2,    llvm::AArch64::Q1_Q2_Q3,\n    llvm::AArch64::Q2_Q3_Q4,    llvm::AArch64::Q3_Q4_Q5,\n    llvm::AArch64::Q4_Q5_Q6,    llvm::AArch64::Q5_Q6_Q7,\n    llvm::AArch64::Q6_Q7_Q8,    llvm::AArch64::Q7_Q8_Q9,\n    llvm::AArch64::Q8_Q9_Q10,   llvm::AArch64::Q9_Q10_Q11,\n    llvm::AArch64::Q10_Q11_Q12, llvm::AArch64::Q11_Q12_Q13,\n    llvm::AArch64::Q12_Q13_Q14, llvm::AArch64::Q13_Q14_Q15,\n    llvm::AArch64::Q14_Q15_Q16, llvm::AArch64::Q15_Q16_Q17,\n    llvm::AArch64::Q16_Q17_Q18, llvm::AArch64::Q17_Q18_Q19,\n    llvm::AArch64::Q18_Q19_Q20, llvm::AArch64::Q19_Q20_Q21,\n    llvm::AArch64::Q20_Q21_Q22, llvm::AArch64::Q21_Q22_Q23,\n    llvm::AArch64::Q22_Q23_Q24, llvm::AArch64::Q23_Q24_Q25,\n    llvm::AArch64::Q24_Q25_Q26, llvm::AArch64::Q25_Q26_Q27,\n    llvm::AArch64::Q26_Q27_Q28, llvm::AArch64::Q27_Q28_Q29,\n    llvm::AArch64::Q28_Q29_Q30, llvm::AArch64::Q29_Q30_Q31,\n    llvm::AArch64::Q30_Q31_Q0,  llvm::AArch64::Q31_Q0_Q1,\n};\n\nconstexpr size_t REGISTER_16BYTES_P3_SIZE =\n    sizeof(REGISTER_16BYTES_P3) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES_P4[] = {\n    llvm::AArch64::Q0_Q1_Q2_Q3,     llvm::AArch64::Q1_Q2_Q3_Q4,\n    llvm::AArch64::Q2_Q3_Q4_Q5,     llvm::AArch64::Q3_Q4_Q5_Q6,\n    llvm::AArch64::Q4_Q5_Q6_Q7,     llvm::AArch64::Q5_Q6_Q7_Q8,\n    llvm::AArch64::Q6_Q7_Q8_Q9,     llvm::AArch64::Q7_Q8_Q9_Q10,\n    llvm::AArch64::Q8_Q9_Q10_Q11,   llvm::AArch64::Q9_Q10_Q11_Q12,\n    llvm::AArch64::Q10_Q11_Q12_Q13, llvm::AArch64::Q11_Q12_Q13_Q14,\n    llvm::AArch64::Q12_Q13_Q14_Q15, llvm::AArch64::Q13_Q14_Q15_Q16,\n    llvm::AArch64::Q14_Q15_Q16_Q17, llvm::AArch64::Q15_Q16_Q17_Q18,\n    llvm::AArch64::Q16_Q17_Q18_Q19, llvm::AArch64::Q17_Q18_Q19_Q20,\n    llvm::AArch64::Q18_Q19_Q20_Q21, llvm::AArch64::Q19_Q20_Q21_Q22,\n    llvm::AArch64::Q20_Q21_Q22_Q23, llvm::AArch64::Q21_Q22_Q23_Q24,\n    llvm::AArch64::Q22_Q23_Q24_Q25, llvm::AArch64::Q23_Q24_Q25_Q26,\n    llvm::AArch64::Q24_Q25_Q26_Q27, llvm::AArch64::Q25_Q26_Q27_Q28,\n    llvm::AArch64::Q26_Q27_Q28_Q29, llvm::AArch64::Q27_Q28_Q29_Q30,\n    llvm::AArch64::Q28_Q29_Q30_Q31, llvm::AArch64::Q29_Q30_Q31_Q0,\n    llvm::AArch64::Q30_Q31_Q0_Q1,   llvm::AArch64::Q31_Q0_Q1_Q2,\n};\n\nconstexpr size_t REGISTER_16BYTES_P4_SIZE =\n    sizeof(REGISTER_16BYTES_P4) / sizeof(uint16_t);\n\n/* Encode base register on one byte\n * -1: invalid\n *  0 : X0 ... 31 : X31/SP  (special 32 : XZR)\n * 33 : Q0 ... 64 : Q31     (special 65|66 : FPCR|FPSR)\n */\nconstexpr int8_t getEncodedBaseReg(unsigned reg) {\n  // verify llvm register enum\n  static_assert((llvm::AArch64::B31 - llvm::AArch64::B0) == 31);\n  static_assert((llvm::AArch64::D31 - llvm::AArch64::D0) == 31);\n  static_assert((llvm::AArch64::H31 - llvm::AArch64::H0) == 31);\n  static_assert((llvm::AArch64::Q31 - llvm::AArch64::Q0) == 31);\n  static_assert((llvm::AArch64::S31 - llvm::AArch64::S0) == 31);\n  static_assert((llvm::AArch64::W30 - llvm::AArch64::W0) == 30);\n  static_assert((llvm::AArch64::X28 - llvm::AArch64::X0) == 28);\n  static_assert((llvm::AArch64::D31_D0 - llvm::AArch64::D0_D1) == 31);\n  static_assert((llvm::AArch64::D31_D0_D1 - llvm::AArch64::D0_D1_D2) == 31);\n  static_assert((llvm::AArch64::D31_D0_D1_D2 - llvm::AArch64::D0_D1_D2_D3) ==\n                31);\n  static_assert((llvm::AArch64::Q31_Q0 - llvm::AArch64::Q0_Q1) == 31);\n  static_assert((llvm::AArch64::Q31_Q0_Q1 - llvm::AArch64::Q0_Q1_Q2) == 31);\n  static_assert((llvm::AArch64::Q31_Q0_Q1_Q2 - llvm::AArch64::Q0_Q1_Q2_Q3) ==\n                31);\n  static_assert((llvm::AArch64::W28_W29 - llvm::AArch64::W0_W1) == 14);\n  static_assert((llvm::AArch64::X26_X27 - llvm::AArch64::X0_X1) == 13);\n  static_assert((llvm::AArch64::X20_X21_X22_X23_X24_X25_X26_X27 -\n                 llvm::AArch64::X0_X1_X2_X3_X4_X5_X6_X7) == 10);\n\n  if (llvm::AArch64::B0 <= reg and reg <= llvm::AArch64::B31) {\n    return 33 + (reg - llvm::AArch64::B0);\n  } else if (llvm::AArch64::D0 <= reg and reg <= llvm::AArch64::D31) {\n    return 33 + (reg - llvm::AArch64::D0);\n  } else if (llvm::AArch64::H0 <= reg and reg <= llvm::AArch64::H31) {\n    return 33 + (reg - llvm::AArch64::H0);\n  } else if (llvm::AArch64::Q0 <= reg and reg <= llvm::AArch64::Q31) {\n    return 33 + (reg - llvm::AArch64::Q0);\n  } else if (llvm::AArch64::S0 <= reg and reg <= llvm::AArch64::S31) {\n    return 33 + (reg - llvm::AArch64::S0);\n  } else if (llvm::AArch64::W0 <= reg and reg <= llvm::AArch64::W30) {\n    return 0 + (reg - llvm::AArch64::W0);\n  } else if (llvm::AArch64::X0 <= reg and reg <= llvm::AArch64::X28) {\n    return 0 + (reg - llvm::AArch64::X0);\n  } else if (llvm::AArch64::D0_D1 <= reg and reg <= llvm::AArch64::D31_D0) {\n    return 33 + (reg - llvm::AArch64::D0_D1);\n  } else if (llvm::AArch64::D0_D1_D2 <= reg and\n             reg <= llvm::AArch64::D31_D0_D1) {\n    return 33 + (reg - llvm::AArch64::D0_D1_D2);\n  } else if (llvm::AArch64::D0_D1_D2_D3 <= reg and\n             reg <= llvm::AArch64::D31_D0_D1_D2) {\n    return 33 + (reg - llvm::AArch64::D0_D1_D2_D3);\n  } else if (llvm::AArch64::Q0_Q1 <= reg and reg <= llvm::AArch64::Q31_Q0) {\n    return 33 + (reg - llvm::AArch64::Q0_Q1);\n  } else if (llvm::AArch64::Q0_Q1_Q2 <= reg and\n             reg <= llvm::AArch64::Q31_Q0_Q1) {\n    return 33 + (reg - llvm::AArch64::Q0_Q1_Q2);\n  } else if (llvm::AArch64::Q0_Q1_Q2_Q3 <= reg and\n             reg <= llvm::AArch64::Q31_Q0_Q1_Q2) {\n    return 33 + (reg - llvm::AArch64::Q0_Q1_Q2_Q3);\n  } else if (llvm::AArch64::W0_W1 <= reg and reg <= llvm::AArch64::W28_W29) {\n    return 0 + (reg - llvm::AArch64::W0_W1) * 2;\n  } else if (llvm::AArch64::X0_X1 <= reg and reg <= llvm::AArch64::X26_X27) {\n    return 0 + (reg - llvm::AArch64::X0_X1) * 2;\n  } else if (llvm::AArch64::X0_X1_X2_X3_X4_X5_X6_X7 <= reg and\n             reg <= llvm::AArch64::X20_X21_X22_X23_X24_X25_X26_X27) {\n    return 0 + (reg - llvm::AArch64::X0_X1_X2_X3_X4_X5_X6_X7) * 2;\n  } else {\n    switch (reg) {\n      case llvm::AArch64::X22_X23_X24_X25_X26_X27_X28_FP:\n        return 22;\n      case llvm::AArch64::X28_FP:\n        return 28;\n      case llvm::AArch64::FP:\n        return 29;\n      case llvm::AArch64::LR:\n      case llvm::AArch64::W30_WZR:\n      case llvm::AArch64::LR_XZR:\n        return 30;\n      case llvm::AArch64::WSP:\n      case llvm::AArch64::SP:\n        return 31;\n      case llvm::AArch64::WZR:\n      case llvm::AArch64::XZR:\n        return 32;\n      case llvm::AArch64::FPCR:\n        return 65;\n      case llvm::AArch64::FPSR:\n        return 66;\n      default:\n        return -1;\n    }\n  }\n}\n\nstruct RegisterInfoArray {\n  uint16_t sizeArr[llvm::AArch64::NUM_TARGET_REGS] = {0};\n\n  // Use to find the Higher register\n  // -1: invalid\n  //  0 : X0 ... 31 : X31/SP  (special 32 : XZR)\n  // 33 : Q0 ... 64 : Q31     (special 65|66 : FPCR|FPSR)\n  int8_t baseReg[llvm::AArch64::NUM_TARGET_REGS] = {0};\n\n  constexpr uint16_t setValue(uint8_t size, uint8_t packed) {\n    return (size & 0xff) | ((packed & 0xff) << 8);\n  }\n\n  constexpr RegisterInfoArray() {\n    for (size_t i = 0; i < REGISTER_1BYTE_SIZE; i++) {\n      sizeArr[REGISTER_1BYTE[i]] = setValue(1, 1);\n    }\n    for (size_t i = 0; i < REGISTER_2BYTES_SIZE; i++) {\n      sizeArr[REGISTER_2BYTES[i]] = setValue(2, 1);\n    }\n    for (size_t i = 0; i < REGISTER_4BYTES_SIZE; i++) {\n      sizeArr[REGISTER_4BYTES[i]] = setValue(4, 1);\n    }\n    for (size_t i = 0; i < REGISTER_4BYTES_P2_SIZE; i++) {\n      sizeArr[REGISTER_4BYTES_P2[i]] = setValue(4, 2);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES[i]] = setValue(8, 1);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P2_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P2[i]] = setValue(8, 2);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P3_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P3[i]] = setValue(8, 3);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P4_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P4[i]] = setValue(8, 4);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P8_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P8[i]] = setValue(8, 8);\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_SIZE; i++) {\n      sizeArr[REGISTER_16BYTES[i]] = setValue(16, 1);\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_P2_SIZE; i++) {\n      sizeArr[REGISTER_16BYTES_P2[i]] = setValue(16, 2);\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_P3_SIZE; i++) {\n      sizeArr[REGISTER_16BYTES_P3[i]] = setValue(16, 3);\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_P4_SIZE; i++) {\n      sizeArr[REGISTER_16BYTES_P4[i]] = setValue(16, 4);\n    }\n    for (size_t i = 0; i < llvm::AArch64::NUM_TARGET_REGS; i++) {\n      baseReg[i] = getEncodedBaseReg(i);\n    }\n  }\n\n  inline uint8_t getSize(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg < llvm::AArch64::NUM_TARGET_REGS)\n      return sizeArr[reg] & 0xff;\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return 0;\n  }\n\n  inline uint8_t getPacked(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg < llvm::AArch64::NUM_TARGET_REGS)\n      return (sizeArr[reg] >> 8) & 0xff;\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return 0;\n  }\n\n  inline RegLLVM getUpperReg(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg >= llvm::AArch64::NUM_TARGET_REGS) {\n      QBDI_ERROR(\"No register {}\", reg);\n      return llvm::AArch64::NoRegister;\n    }\n    int8_t v = baseReg[reg];\n    if (v < 0) {\n      return llvm::AArch64::NoRegister;\n    } else if (v < 29) {\n      static_assert((llvm::AArch64::X28 - llvm::AArch64::X0) == 28);\n      return llvm::AArch64::X0 + v;\n    } else if (v < 33) {\n      switch (v) {\n        case 29:\n          return llvm::AArch64::FP;\n        case 30:\n          return llvm::AArch64::LR;\n        case 31:\n          return llvm::AArch64::SP;\n        case 32:\n          return llvm::AArch64::XZR;\n      }\n    } else if (v < 65) {\n      static_assert((llvm::AArch64::Q31 - llvm::AArch64::Q0) == 31);\n      return llvm::AArch64::Q0 + (v - 33);\n    } else if (v == 65) {\n      return llvm::AArch64::FPCR;\n    } else if (v == 66) {\n      return llvm::AArch64::FPSR;\n    }\n    // invalid positive value\n    QBDI_ERROR(\"Wrong value {}\", v);\n    return llvm::AArch64::NoRegister;\n  }\n\n  inline size_t getGPRPos(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg >= llvm::AArch64::NUM_TARGET_REGS) {\n      QBDI_ERROR(\"No register {}\", reg);\n      return -1;\n    }\n    int8_t v = baseReg[reg];\n    if (v < 0 || v > 31) {\n      return -1;\n    } else {\n      return v;\n    }\n  }\n\n  inline RegLLVM getUpperBasedRegister(RegLLVM reg) const {\n    RegLLVM r = getUpperReg(reg);\n    if (r == llvm::AArch64::NoRegister) {\n      return reg;\n    }\n    return r;\n  }\n};\n\nconstexpr RegisterInfoArray arrayInfo;\n\n} // anonymous namespace\n\nuint8_t getRegisterSize(RegLLVM reg) { return arrayInfo.getSize(reg); }\n\nuint8_t getRegisterBaseOffset(RegLLVM reg) { return 0; }\n\nuint8_t getRegisterPacked(RegLLVM reg) { return arrayInfo.getPacked(reg); }\n\nuint8_t getRegisterSpaced(RegLLVM reg) { return 1; }\n\nsize_t getGPRPosition(RegLLVM reg) { return arrayInfo.getGPRPos(reg); }\n\nRegLLVM getUpperRegister(RegLLVM reg, size_t pos) {\n  if (pos == 0) {\n    return arrayInfo.getUpperBasedRegister(reg);\n  }\n  if (pos >= getRegisterPacked(reg)) {\n    return llvm::AArch64::NoRegister;\n  }\n  unsigned r = arrayInfo.getUpperBasedRegister(reg).getValue();\n  if (llvm::AArch64::Q0 <= r and r <= llvm::AArch64::Q31) {\n    return llvm::AArch64::Q0 + ((pos + (r - llvm::AArch64::Q0)) % 32);\n  }\n\n  unsigned p;\n  if (llvm::AArch64::X0 <= r and r <= llvm::AArch64::X28) {\n    p = r - llvm::AArch64::X0;\n  } else {\n    switch (r) {\n      case llvm::AArch64::FP:\n        p = 29;\n        break;\n      case llvm::AArch64::LR:\n        p = 30;\n        break;\n      case llvm::AArch64::XZR:\n        p = 31;\n        break;\n      case llvm::AArch64::SP:\n      default:\n        QBDI_ERROR(\"Unexpected Packed Register {} {}\", reg.getValue(), r);\n        return llvm::AArch64::NoRegister;\n    }\n  }\n  p = (p + pos) % 32;\n  if (p < 29) {\n    return llvm::AArch64::X0 + p;\n  }\n  switch (p) {\n    case 29:\n      return llvm::AArch64::FP;\n    case 30:\n      return llvm::AArch64::LR;\n    case 31:\n      return llvm::AArch64::XZR;\n    default:\n      QBDI_ERROR(\"Unexpected Packed Register {} {}\", reg.getValue(), r);\n      return llvm::AArch64::NoRegister;\n  }\n}\n\nRegLLVM getPackedRegister(RegLLVM reg, size_t pos) {\n  if (pos == 0 and getRegisterPacked(reg) == 1) {\n    return reg;\n  }\n  if (pos >= getRegisterPacked(reg)) {\n    return llvm::AArch64::NoRegister;\n  }\n  unsigned r = getUpperRegister(reg, pos).getValue();\n  // for FPR register\n  if (llvm::AArch64::Q0 <= r and r <= llvm::AArch64::Q31) {\n    switch (getRegisterSize(reg)) {\n      case 1:\n        return llvm::AArch64::B0 + (r - llvm::AArch64::Q0);\n      case 2:\n        return llvm::AArch64::H0 + (r - llvm::AArch64::Q0);\n      case 4:\n        return llvm::AArch64::S0 + (r - llvm::AArch64::Q0);\n      case 8:\n        return llvm::AArch64::D0 + (r - llvm::AArch64::Q0);\n      case 16:\n        return llvm::AArch64::Q0 + (r - llvm::AArch64::Q0);\n      default:\n        QBDI_ERROR(\"Unexpected size {} for Packed Register {} {}\",\n                   getRegisterSize(reg), reg.getValue(), r);\n        return llvm::AArch64::NoRegister;\n    }\n  }\n  // for GPR register\n  if ((llvm::AArch64::X0 <= r and r <= llvm::AArch64::X28) or\n      r == llvm::AArch64::FP or r == llvm::AArch64::LR or\n      r == llvm::AArch64::XZR) {\n\n    switch (getRegisterSize(reg)) {\n      case 4: {\n        if (llvm::AArch64::X0 <= r and r <= llvm::AArch64::X28) {\n          return llvm::AArch64::W0 + (r - llvm::AArch64::X0);\n        }\n        switch (r) {\n          case llvm::AArch64::FP:\n            return llvm::AArch64::W29;\n          case llvm::AArch64::LR:\n            return llvm::AArch64::W30;\n          case llvm::AArch64::XZR:\n            return llvm::AArch64::WZR;\n          default:\n            QBDI_ERROR(\"Unexpected size {} for Packed Register {} {}\",\n                       getRegisterSize(reg), reg.getValue(), r);\n            return llvm::AArch64::NoRegister;\n        }\n      }\n      case 8:\n        return r;\n      default:\n        QBDI_ERROR(\"Unexpected size {} for Packed Register {} {}\",\n                   getRegisterSize(reg), reg.getValue(), r);\n        return llvm::AArch64::NoRegister;\n    }\n  }\n\n  QBDI_ERROR(\"Unexpected Packed Register {} {}\", reg.getValue(), r);\n  return llvm::AArch64::NoRegister;\n}\n\nvoid fixLLVMUsedGPR(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                    std::array<RegisterUsage, NUM_GPR> &arr,\n                    std::map<RegLLVM, RegisterUsage> &m) {}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/RelocatableInst_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdint.h>\n\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/AARCH64/Layer2_AARCH64.h\"\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\n// Generic RelocatableInst that must be implemented by each target\n\n// RelocTag\n// ========\n\nllvm::MCInst RelocTag::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  QBDI_ERROR(\"Internal Error: Relocate a Tag instruction.\");\n  return nop();\n}\n\n// LoadShadow\n// ==========\n\nllvm::MCInst LoadShadow::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id = execBlock->getLastShadow(tag);\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  return ldr(reg, sr, shadowOffset);\n}\n\nint LoadShadow::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// StoreShadow\n// ===========\n\nllvm::MCInst StoreShadow::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id;\n  if (create) {\n    id = execBlock->newShadow(tag);\n  } else {\n    id = execBlock->getLastShadow(tag);\n  }\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  return str(reg, sr, shadowOffset);\n}\n\nint StoreShadow::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// LoadDataBlock\n// =============\n\nllvm::MCInst LoadDataBlock::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  return ldr(reg, sr, offset);\n}\n\nint LoadDataBlock::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// StoreDataBlock\n// ==============\n\nllvm::MCInst StoreDataBlock::reloc(ExecBlock *execBlock,\n                                   CPUMode cpumode) const {\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  return str(reg, sr, offset);\n}\n\nint StoreDataBlock::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// MovReg\n// ======\n\nllvm::MCInst MovReg::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  return movrr(dst, src);\n}\n\nint MovReg::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// LoadImm\n// =======\n\nllvm::MCInst LoadImm::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  if (imm > 0xFFFF) {\n    uint16_t id = execBlock->newShadow();\n    execBlock->setShadow(id, imm);\n    rword offset = execBlock->getShadowOffset(id);\n    RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n\n    return ldr(reg, sr, offset);\n  }\n  return movri(reg, imm);\n}\n\nint LoadImm::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// InstId\n// ======\n\nllvm::MCInst InstId::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t v = execBlock->getNextInstID();\n  QBDI_REQUIRE_ABORT(v <= 0xFFFF, \"Unexpected InstID {}\", v);\n\n  return movri(reg, v);\n}\n\nint InstId::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// Target Specific RelocatableInst\n\n// SetScratchRegister\n// ================\nllvm::MCInst SetScratchRegister::reloc(ExecBlock *execBlock,\n                                       CPUMode cpumode) const {\n  llvm::MCInst res = inst;\n  QBDI_REQUIRE_ABORT(opn < res.getNumOperands(), \"Invalid operand {}\", opn);\n  res.getOperand(opn).setReg(\n      execBlock->getScratchRegisterInfo().writeScratchRegister.getValue());\n  return res;\n}\n\n// EpilogueAddrRel\n// ===============\nllvm::MCInst EpilogueAddrRel::reloc(ExecBlock *execBlock,\n                                    CPUMode cpumode) const {\n  rword target = execBlock->getEpilogueOffset() + offset;\n  if (target % 4 != 0) {\n    QBDI_CRITICAL(\"Bad alignment!\");\n  }\n  llvm::MCInst res = inst;\n  QBDI_REQUIRE_ABORT(opn < res.getNumOperands(), \"Invalid operand {}\", opn);\n  res.getOperand(opn).setImm(target / 4);\n  return res;\n}\n\n// RestoreScratchRegister\n// ======================\n\nllvm::MCInst RestoreScratchRegister::reloc(ExecBlock *execBlock,\n                                           CPUMode cpumode) const {\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  return ldr(sr, sr, offsetof(Context, hostState.scratchRegisterValue));\n}\n\n// ResetScratchRegister\n// ====================\n\nllvm::MCInst ResetScratchRegister::reloc(ExecBlock *execBlock,\n                                         CPUMode cpumode) const {\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  unsigned offset =\n      execBlock->getDataBlockBase() - (execBlock->getCurrentPC() & ~0xfff);\n  return adrp(sr, offset);\n}\n\n// SetBaseAddress\n// ==============\nllvm::MCInst SetBaseAddress::reloc(ExecBlock *execBlock,\n                                   CPUMode cpumode) const {\n  unsigned offset =\n      execBlock->getDataBlockBase() - (execBlock->getCurrentPC() & ~0xfff);\n  return adrp(reg, offset);\n}\n\n// LoadDataBlockX2\n// ===============\n\nllvm::MCInst LoadDataBlockX2::reloc(ExecBlock *execBlock,\n                                    CPUMode cpumode) const {\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  return ldp(reg, reg2, sr, offset);\n}\n\n// StoreDataBlockX2\n// ================\n\nllvm::MCInst StoreDataBlockX2::reloc(ExecBlock *execBlock,\n                                     CPUMode cpumode) const {\n  RegLLVM sr = execBlock->getScratchRegisterInfo().writeScratchRegister;\n  return stp(reg, reg2, sr, offset);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/AARCH64/RelocatableInst_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef RELOCATABLEINST_AARCH64_H\n#define RELOCATABLEINST_AARCH64_H\n\n#include <memory>\n#include <utility>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass ExecBlock;\n\nclass SetScratchRegister\n    : public AutoClone<RelocatableInst, SetScratchRegister> {\n  llvm::MCInst inst;\n  Operand opn;\n\npublic:\n  SetScratchRegister(llvm::MCInst &&inst, Operand opn)\n      : AutoClone<RelocatableInst, SetScratchRegister>(),\n        inst(std::forward<llvm::MCInst>(inst)), opn(opn) {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 4; }\n};\n\nclass EpilogueAddrRel : public AutoClone<RelocatableInst, EpilogueAddrRel> {\n  llvm::MCInst inst;\n  Operand opn;\n  rword offset;\n\npublic:\n  EpilogueAddrRel(llvm::MCInst &&inst, Operand opn, rword offset)\n      : AutoClone<RelocatableInst, EpilogueAddrRel>(),\n        inst(std::forward<llvm::MCInst>(inst)), opn(opn), offset(offset) {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 4; }\n};\n\nclass RestoreScratchRegister\n    : public AutoClone<RelocatableInst, RestoreScratchRegister> {\n\npublic:\n  RestoreScratchRegister() {}\n\n  // Restore the original value of the scratch register\n  // /!\\ After this instruction, only use NoReloc\n  // until the scratch register is restored with ResetScratchRegister\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 4; }\n};\n\nclass ResetScratchRegister\n    : public AutoClone<RelocatableInst, ResetScratchRegister> {\n\npublic:\n  ResetScratchRegister() {}\n\n  // Set the Datablock address in the ScratchRegister\n  // The value of the scratch register is lost\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 4; }\n};\n\nclass SetBaseAddress : public AutoClone<RelocatableInst, SetBaseAddress> {\n  RegLLVM reg;\n\npublic:\n  SetBaseAddress(RegLLVM reg) : reg(reg) {}\n\n  // Set the Datablock address in the ScratchRegister\n  // The value of the scratch register is lost\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 4; }\n};\n\nclass LoadDataBlockX2 : public AutoClone<RelocatableInst, LoadDataBlockX2> {\n  RegLLVM reg;\n  RegLLVM reg2;\n  int64_t offset;\n\npublic:\n  LoadDataBlockX2(RegLLVM reg, RegLLVM reg2, int64_t offset)\n      : AutoClone<RelocatableInst, LoadDataBlockX2>(), reg(reg), reg2(reg2),\n        offset(offset) {}\n\n  // Load a value from the specified offset of the datablock\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 4; }\n};\n\nclass StoreDataBlockX2 : public AutoClone<RelocatableInst, StoreDataBlockX2> {\n  RegLLVM reg;\n  RegLLVM reg2;\n  int64_t offset;\n\npublic:\n  StoreDataBlockX2(RegLLVM reg, RegLLVM reg2, int64_t offset)\n      : AutoClone<RelocatableInst, StoreDataBlockX2>(), reg(reg), reg2(reg2),\n        offset(offset) {}\n\n  // Store a value to the specified offset of the datablock\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 4; }\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/TempManagerImpl_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef TempManager_AARCH64_H\n#define TempManager_AARCH64_H\n\n#include <set>\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nstatic const std::set<Reg> TempManagerUnrestoreGPR = {Reg(28)};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/Patch/AARCH64/TempManager_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <set>\n#include \"Patch/AARCH64/RelocatableInst_AARCH64.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nvoid TempManager::generateSaveRestoreInstructions(\n    unsigned unrestoredRegNum, RelocatableInst::UniquePtrVec &saveInst,\n    RelocatableInst::UniquePtrVec &restoreInst, Reg::Vec &unrestoredReg) const {\n\n  saveInst.clear();\n  restoreInst.clear();\n  unrestoredReg.clear();\n\n  Reg::Vec usedRegisters = getUsedRegisters();\n\n  std::sort(usedRegisters.begin(), usedRegisters.end(),\n            [](const Reg &a, const Reg &b) { return a.getID() < b.getID(); });\n\n  for (Reg r : usedRegisters) {\n    if (not shouldRestore(r)) {\n      unrestoredReg.push_back(r);\n    }\n  }\n\n  std::vector<std::pair<Reg, Reg>> pairRegister;\n\n  for (unsigned i = 0; i < usedRegisters.size(); i++) {\n    Reg r = usedRegisters[i];\n    if (shouldRestore(r)) {\n      // found a pair register that we may optimised with LDP/STP\n      if (i + 1 < usedRegisters.size() and\n          shouldRestore(usedRegisters[i + 1]) and\n          r.getID() + 1 == usedRegisters[i + 1].getID()) {\n        saveInst.push_back(\n            StoreDataBlockX2::unique(r, usedRegisters[i + 1], Offset(r)));\n        pairRegister.emplace_back(r, usedRegisters[i + 1]);\n        i++;\n      } else if (unrestoredReg.size() < unrestoredRegNum) {\n        append(saveInst, SaveReg(r, Offset(r)).genReloc(*patch.llvmcpu));\n        unrestoredReg.push_back(r);\n      } else {\n        append(saveInst, SaveReg(r, Offset(r)).genReloc(*patch.llvmcpu));\n        append(restoreInst, LoadReg(r, Offset(r)).genReloc(*patch.llvmcpu));\n      }\n    }\n  }\n\n  for (const auto &p : pairRegister) {\n    if (unrestoredReg.size() < unrestoredRegNum) {\n      unrestoredReg.push_back(p.first);\n      if (unrestoredReg.size() < unrestoredRegNum) {\n        unrestoredReg.push_back(p.second);\n      } else {\n        append(restoreInst,\n               LoadReg(p.second, Offset(p.second)).genReloc(*patch.llvmcpu));\n      }\n    } else {\n      restoreInst.push_back(\n          LoadDataBlockX2::unique(p.first, p.second, Offset(p.first)));\n    }\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/CMakeLists.txt",
    "content": "set(SOURCES\n    \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockFlags_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockPatch_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstInfo_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstrRules_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Layer2_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccess_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchCondition_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchGenerator_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchRuleAssembly_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Register_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/RelocatableInst_ARM.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/TempManager_ARM.cpp\")\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Patch/ARM/ExecBlockFlags_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Patch/ExecBlockFlags.h\"\n\nnamespace QBDI {\n\nconst uint8_t defaultExecuteFlags = ExecBlockFlags::Default;\n\nuint8_t getExecBlockFlags(const llvm::MCInst &inst,\n                          const QBDI::LLVMCPU &llvmcpu) {\n  return defaultExecuteFlags;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/ExecBlockFlags_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef ExecBlockFlags_ARM_H\n#define ExecBlockFlags_ARM_H\n\n#include <stdint.h>\n\nnamespace QBDI {\n\ntypedef enum : uint8_t { Default = 0 } ExecBlockFlags;\n\n}\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/ExecBlockPatch_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Engine/LLVMCPU.h\"\n\n#include \"Patch/ARM/ExecBlockPatch_ARM.h\"\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"Patch/ARM/PatchGenerator_ARM.h\"\n#include \"Patch/ARM/RelocatableInst_ARM.h\"\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"QBDI/Options.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nRelocatableInst::UniquePtrVec getExecBlockPrologue(const LLVMCPU &llvmcpu) {\n  RelocatableInst::UniquePtrVec prologue;\n\n  // prologue.push_back(Bkpt(CPUMode::ARM, 0));\n\n  // Save host LR\n  // ============\n  prologue.push_back(Pushr1(CPUMode::ARM, Reg(REG_LR)));\n\n  // Save host SP\n  // ============\n  append(prologue, SaveReg(Reg(REG_SP), Offset(offsetof(Context, hostState.sp)))\n                       .genReloc(llvmcpu));\n\n  // set R0 to the address of the dataBlock\n  append(prologue, SetDataBlockAddress(Reg(0)).genReloc(llvmcpu));\n\n  if (not llvmcpu.hasOptions(Options::OPT_DISABLE_FPR)) {\n    // set R1 at the begin of FPRState\n    prologue.push_back(Add(CPUMode::ARM, Reg(1), Reg(0),\n                           Constant(offsetof(Context, fprState))));\n    // load fpscr in R2\n    prologue.push_back(\n        NoReloc::unique(ldri12(Reg(2), Reg(1), offsetof(FPRState, fpscr))));\n\n    // Restore FPR\n    // ===========\n    prologue.push_back(VLdmIA(CPUMode::ARM, Reg(1), 0, 16, true));\n#if QBDI_NUM_FPR == 32\n    if (not llvmcpu.hasOptions(Options::OPT_DISABLE_D16_D31)) {\n      prologue.push_back(VLdmIA(CPUMode::ARM, Reg(1), 16, 16));\n    }\n#endif\n\n    // Restore FPSCR\n    // =============\n    prologue.push_back(Vmsr(CPUMode::ARM, Reg(2)));\n  }\n\n  // set R0 at the begin of GPRState\n  prologue.push_back(\n      Add(CPUMode::ARM, Reg(0), Reg(0), Constant(offsetof(Context, gprState))));\n\n  // Restore CPSR\n  // ============\n  prologue.push_back(\n      NoReloc::unique(ldri12(Reg(1), Reg(0), offsetof(GPRState, cpsr))));\n  prologue.push_back(Msr(CPUMode::ARM, Reg(1)));\n\n  // Restore GPR\n  // ===========\n\n  prologue.push_back(\n      LdmIA(CPUMode::ARM, Reg(0), /* R0-R12 + SP + LR */ 0b0111111111111111));\n\n  // Jump selector\n  // =============\n  prologue.push_back(LoadDataBlock::unique(\n      Reg(REG_PC), Offset(offsetof(Context, hostState.selector))));\n\n  return prologue;\n}\n\nRelocatableInst::UniquePtrVec getExecBlockEpilogue(const LLVMCPU &llvmcpu) {\n  RelocatableInst::UniquePtrVec epilogue;\n\n  // Save R0\n  // =======\n  append(epilogue, SaveReg(Reg(0), Offset(offsetof(Context, gprState.r0)))\n                       .genReloc(llvmcpu));\n\n  // set R0 to the address of the dataBlock\n  append(epilogue, SetDataBlockAddress(Reg(0)).genReloc(llvmcpu));\n  // Set R0 to GPRState.r1\n  epilogue.push_back(Add(CPUMode::ARM, Reg(0), Reg(0),\n                         Constant(offsetof(Context, gprState.r1))));\n\n  // Save R1-R12 SP LR\n  epilogue.push_back(\n      StmIA(CPUMode::ARM, Reg(0), /* R1-R12 + SP + LR */ 0b0111111111111110));\n\n  // Save CPSR\n  // =========\n  epilogue.push_back(Mrs(CPUMode::ARM, Reg(1)));\n  epilogue.push_back(\n      StoreDataBlock::unique(Reg(1), Offset(offsetof(Context, gprState.cpsr))));\n\n  if (not llvmcpu.hasOptions(Options::OPT_DISABLE_FPR)) {\n    // set R1 at the begin of FPRState (from the gprState.r1)\n    epilogue.push_back(Add(CPUMode::ARM, Reg(1), Reg(0),\n                           Constant(offsetof(Context, fprState) -\n                                    offsetof(Context, gprState.r1))));\n    // Restore FPSCR\n    // =============\n    epilogue.push_back(Vmrs(CPUMode::ARM, Reg(2)));\n    epilogue.push_back(StoreDataBlock::unique(\n        Reg(2), Offset(offsetof(Context, fprState.fpscr))));\n\n    // Save FPR\n    // ========\n    epilogue.push_back(VStmIA(CPUMode::ARM, Reg(1), 0, 16, true));\n#if QBDI_NUM_FPR == 32\n    if (not llvmcpu.hasOptions(Options::OPT_DISABLE_D16_D31)) {\n      epilogue.push_back(VStmIA(CPUMode::ARM, Reg(1), 16, 16));\n    }\n#endif\n  }\n\n  // Restore host SP\n  // ===============\n  append(epilogue, LoadReg(Reg(REG_SP), Offset(offsetof(Context, hostState.sp)))\n                       .genReloc(llvmcpu));\n\n  // Return to host\n  // ==============\n  epilogue.push_back(Popr1(CPUMode::ARM, Reg(REG_PC)));\n\n  return epilogue;\n}\n\n// Patch allowing to terminate a basic block early by writing address into\n// DataBlock[Offset(PC)]\nRelocatableInst::UniquePtrVec getTerminator(const LLVMCPU &llvmcpu,\n                                            rword address) {\n  RelocatableInst::UniquePtrVec terminator;\n\n  // Set the LSB to the CPUMode\n  if (llvmcpu.getCPUMode() == CPUMode::ARM) {\n    address &= (~1);\n  } else {\n    address |= 1;\n  }\n\n  // for thumb, any register can be used to be the scratch register\n  // except LR. Used LR to saved the next address\n  append(terminator,\n         SaveReg(Reg(REG_LR), Offset(Reg(REG_LR))).genReloc(llvmcpu));\n  terminator.push_back(LoadImm::unique(Reg(REG_LR), address));\n  append(terminator,\n         SaveReg(Reg(REG_LR), Offset(Reg(REG_PC))).genReloc(llvmcpu));\n  append(terminator,\n         LoadReg(Reg(REG_LR), Offset(Reg(REG_LR))).genReloc(llvmcpu));\n\n  return terminator;\n}\n\n// Change ScratchRegister\nRelocatableInst::UniquePtrVec\nchangeScratchRegister(const LLVMCPU &llvmcpu, RegLLVM oldSR, RegLLVM nextSR_) {\n\n  QBDI_REQUIRE_ABORT(llvmcpu == CPUMode::Thumb,\n                     \"No scratch Register in ARM mode\");\n\n  QBDI_REQUIRE_ABORT(getGPRPosition(nextSR_) != ((size_t)-1),\n                     \"Unexpected next ScratchRegister {}\",\n                     llvmcpu.getRegisterName(nextSR_));\n\n  Reg nextSR = getGPRPosition(nextSR_);\n\n  // get another temporary register\n  Reg tmp = 0;\n  while (tmp == oldSR or tmp == nextSR) {\n    tmp = tmp.getID() + 1;\n  }\n\n  RelocatableInst::UniquePtrVec changeReloc;\n\n  changeReloc.push_back(RelocTag::unique(RelocTagChangeScratchRegister));\n\n  // save the temporary register\n  changeReloc.push_back(NoReloc::unique(t2stri12(tmp, oldSR, tmp.offset())));\n  // load the real value of the old SR\n  changeReloc.push_back(NoReloc::unique(\n      t2ldri12(tmp, oldSR, offsetof(Context, hostState.scratchRegisterValue))));\n  // backup the real value of the next SR\n  changeReloc.push_back(NoReloc::unique(t2stri12(\n      nextSR, oldSR, offsetof(Context, hostState.scratchRegisterValue))));\n  // change the SR\n  changeReloc.push_back(NoReloc::unique(tmovr(nextSR, oldSR)));\n  // restore the value of the old SR\n  changeReloc.push_back(NoReloc::unique(tmovr(oldSR, tmp)));\n  // change the index of the SRregister\n  changeReloc.push_back(NoReloc::unique(t2movi(tmp, nextSR.getID())));\n  changeReloc.push_back(NoReloc::unique(\n      t2stri12(tmp, nextSR, offsetof(Context, hostState.currentSROffset))));\n  // restore the temporary register\n  changeReloc.push_back(NoReloc::unique(t2ldri12(tmp, nextSR, tmp.offset())));\n\n  return changeReloc;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/ExecBlockPatch_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef EXECBLOCKPATCH_ARM_H\n#define EXECBLOCKPATCH_ARM_H\n\n#include <memory>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nclass RelocatableInst;\nclass LLVMCPU;\n\nstd::vector<std::unique_ptr<RelocatableInst>>\nchangeScratchRegister(const LLVMCPU &llvmcpu, RegLLVM oldSR, RegLLVM nextSR);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/InstInfo_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <set>\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"ARMInstrInfo.h\"\n#include \"MCTargetDesc/ARMAddressingModes.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"devVariable.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/ARM/InstInfo_ARM.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/InstAnalysis_prive.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\nnamespace {\n\n// Read Instructions\n// =================\n\nconstexpr unsigned READ_8[] = {\n    // clang-format off\n    llvm::ARM::LDAB,\n    llvm::ARM::LDAEXB,\n    llvm::ARM::LDRBT_POST_IMM,\n    llvm::ARM::LDRBT_POST_REG,\n    llvm::ARM::LDRB_POST_IMM,\n    llvm::ARM::LDRB_POST_REG,\n    llvm::ARM::LDRB_PRE_IMM,\n    llvm::ARM::LDRB_PRE_REG,\n    llvm::ARM::LDRBi12,\n    llvm::ARM::LDRBrs,\n    llvm::ARM::LDREXB,\n    llvm::ARM::LDRSB,\n    llvm::ARM::LDRSBTi,\n    llvm::ARM::LDRSBTr,\n    llvm::ARM::LDRSB_POST,\n    llvm::ARM::LDRSB_PRE,\n    llvm::ARM::SWPB,\n    llvm::ARM::VLD1DUPd8,\n    llvm::ARM::VLD1DUPd8wb_fixed,\n    llvm::ARM::VLD1DUPd8wb_register,\n    llvm::ARM::VLD1DUPq8,\n    llvm::ARM::VLD1DUPq8wb_fixed,\n    llvm::ARM::VLD1DUPq8wb_register,\n    llvm::ARM::VLD1LNd8,\n    llvm::ARM::VLD1LNd8_UPD,\n    llvm::ARM::t2LDAB,\n    llvm::ARM::t2LDAEXB,\n    llvm::ARM::t2LDRBT,\n    llvm::ARM::t2LDRB_POST,\n    llvm::ARM::t2LDRB_PRE,\n    llvm::ARM::t2LDRBi12,\n    llvm::ARM::t2LDRBi8,\n    llvm::ARM::t2LDRBpci,\n    llvm::ARM::t2LDRBs,\n    llvm::ARM::t2LDREXB,\n    llvm::ARM::t2LDRSB_POST,\n    llvm::ARM::t2LDRSB_PRE,\n    llvm::ARM::t2LDRSBi12,\n    llvm::ARM::t2LDRSBi8,\n    llvm::ARM::t2LDRSBpci,\n    llvm::ARM::t2LDRSBs,\n    llvm::ARM::t2TBB,\n    llvm::ARM::tLDRBi,\n    llvm::ARM::tLDRBr,\n    llvm::ARM::tLDRSB,\n    // clang-format on\n};\n\nconstexpr size_t READ_8_SIZE = sizeof(READ_8) / sizeof(unsigned);\n\nconstexpr unsigned READ_16[] = {\n    // clang-format off\n    llvm::ARM::LDAEXH,\n    llvm::ARM::LDAH,\n    llvm::ARM::LDREXH,\n    llvm::ARM::LDRH,\n    llvm::ARM::LDRHTi,\n    llvm::ARM::LDRHTr,\n    llvm::ARM::LDRH_POST,\n    llvm::ARM::LDRH_PRE,\n    llvm::ARM::LDRSH,\n    llvm::ARM::LDRSHTi,\n    llvm::ARM::LDRSHTr,\n    llvm::ARM::LDRSH_POST,\n    llvm::ARM::LDRSH_PRE,\n    llvm::ARM::VLD1DUPd16,\n    llvm::ARM::VLD1DUPd16wb_fixed,\n    llvm::ARM::VLD1DUPd16wb_register,\n    llvm::ARM::VLD1DUPq16,\n    llvm::ARM::VLD1DUPq16wb_fixed,\n    llvm::ARM::VLD1DUPq16wb_register,\n    llvm::ARM::VLD1LNd16,\n    llvm::ARM::VLD1LNd16_UPD,\n    llvm::ARM::VLD2DUPd8,\n    llvm::ARM::VLD2DUPd8wb_fixed,\n    llvm::ARM::VLD2DUPd8wb_register,\n    llvm::ARM::VLD2DUPd8x2,\n    llvm::ARM::VLD2DUPd8x2wb_fixed,\n    llvm::ARM::VLD2DUPd8x2wb_register,\n    llvm::ARM::VLD2LNd8,\n    llvm::ARM::VLD2LNd8_UPD,\n    llvm::ARM::VLDRH,\n    llvm::ARM::t2LDAEXH,\n    llvm::ARM::t2LDAH,\n    llvm::ARM::t2LDREXH,\n    llvm::ARM::t2LDRH_POST,\n    llvm::ARM::t2LDRH_PRE,\n    llvm::ARM::t2LDRHi12,\n    llvm::ARM::t2LDRHi8,\n    llvm::ARM::t2LDRHpci,\n    llvm::ARM::t2LDRHs,\n    llvm::ARM::t2LDRSH_POST,\n    llvm::ARM::t2LDRSH_PRE,\n    llvm::ARM::t2LDRSHi12,\n    llvm::ARM::t2LDRSHi8,\n    llvm::ARM::t2LDRSHpci,\n    llvm::ARM::t2LDRSHs,\n    llvm::ARM::t2TBH,\n    llvm::ARM::tLDRHi,\n    llvm::ARM::tLDRHr,\n    llvm::ARM::tLDRSH,\n    // clang-format on\n};\n\nconstexpr size_t READ_16_SIZE = sizeof(READ_16) / sizeof(unsigned);\n\nconstexpr unsigned READ_24[] = {\n    // clang-format off\n    llvm::ARM::VLD3DUPd8,\n    llvm::ARM::VLD3DUPd8_UPD,\n    llvm::ARM::VLD3DUPq8,\n    llvm::ARM::VLD3DUPq8_UPD,\n    llvm::ARM::VLD3LNd8,\n    llvm::ARM::VLD3LNd8_UPD,\n    // clang-format on\n};\n\nconstexpr size_t READ_24_SIZE = sizeof(READ_24) / sizeof(unsigned);\n\nconstexpr unsigned READ_32[] = {\n    // clang-format off\n    llvm::ARM::LDA,\n    llvm::ARM::LDAEX,\n    llvm::ARM::LDREX,\n    llvm::ARM::LDRT_POST_IMM,\n    llvm::ARM::LDRT_POST_REG,\n    llvm::ARM::LDR_POST_IMM,\n    llvm::ARM::LDR_POST_REG,\n    llvm::ARM::LDR_PRE_IMM,\n    llvm::ARM::LDR_PRE_REG,\n    llvm::ARM::LDRi12,\n    llvm::ARM::LDRrs,\n    llvm::ARM::SWP,\n    llvm::ARM::VLD1DUPd32,\n    llvm::ARM::VLD1DUPd32wb_fixed,\n    llvm::ARM::VLD1DUPd32wb_register,\n    llvm::ARM::VLD1DUPq32,\n    llvm::ARM::VLD1DUPq32wb_fixed,\n    llvm::ARM::VLD1DUPq32wb_register,\n    llvm::ARM::VLD1LNd32,\n    llvm::ARM::VLD1LNd32_UPD,\n    llvm::ARM::VLD2DUPd16,\n    llvm::ARM::VLD2DUPd16wb_fixed,\n    llvm::ARM::VLD2DUPd16wb_register,\n    llvm::ARM::VLD2DUPd16x2,\n    llvm::ARM::VLD2DUPd16x2wb_fixed,\n    llvm::ARM::VLD2DUPd16x2wb_register,\n    llvm::ARM::VLD2LNd16,\n    llvm::ARM::VLD2LNd16_UPD,\n    llvm::ARM::VLD2LNq16,\n    llvm::ARM::VLD2LNq16_UPD,\n    llvm::ARM::VLD4DUPd8,\n    llvm::ARM::VLD4DUPd8_UPD,\n    llvm::ARM::VLD4DUPq8,\n    llvm::ARM::VLD4DUPq8_UPD,\n    llvm::ARM::VLD4LNd8,\n    llvm::ARM::VLD4LNd8_UPD,\n    llvm::ARM::VLDRS,\n    llvm::ARM::VLDR_FPCXTNS_off,\n    llvm::ARM::VLDR_FPCXTNS_post,\n    llvm::ARM::VLDR_FPCXTNS_pre,\n    llvm::ARM::VLDR_FPCXTS_off,\n    llvm::ARM::VLDR_FPCXTS_post,\n    llvm::ARM::VLDR_FPCXTS_pre,\n    llvm::ARM::VLDR_FPSCR_NZCVQC_off,\n    llvm::ARM::VLDR_FPSCR_NZCVQC_post,\n    llvm::ARM::VLDR_FPSCR_NZCVQC_pre,\n    llvm::ARM::VLDR_FPSCR_off,\n    llvm::ARM::VLDR_FPSCR_post,\n    llvm::ARM::VLDR_FPSCR_pre,\n    llvm::ARM::VLDR_P0_off,\n    llvm::ARM::VLDR_P0_post,\n    llvm::ARM::VLDR_P0_pre,\n    llvm::ARM::VLDR_VPR_off,\n    llvm::ARM::VLDR_VPR_post,\n    llvm::ARM::VLDR_VPR_pre,\n    llvm::ARM::t2LDA,\n    llvm::ARM::t2LDAEX,\n    llvm::ARM::t2LDREX,\n    llvm::ARM::t2LDRT,\n    llvm::ARM::t2LDR_POST,\n    llvm::ARM::t2LDR_PRE,\n    llvm::ARM::t2LDRi12,\n    llvm::ARM::t2LDRi8,\n    llvm::ARM::t2LDRpci,\n    llvm::ARM::t2LDRs,\n    llvm::ARM::tLDRi,\n    llvm::ARM::tLDRpci,\n    llvm::ARM::tLDRr,\n    llvm::ARM::tLDRspi,\n    // clang-format on\n};\n\nconstexpr size_t READ_32_SIZE = sizeof(READ_32) / sizeof(unsigned);\n\nconstexpr unsigned READ_48[] = {\n    // clang-format off\n    llvm::ARM::VLD3DUPd16,\n    llvm::ARM::VLD3DUPd16_UPD,\n    llvm::ARM::VLD3DUPq16,\n    llvm::ARM::VLD3DUPq16_UPD,\n    llvm::ARM::VLD3LNd16,\n    llvm::ARM::VLD3LNd16_UPD,\n    llvm::ARM::VLD3LNq16,\n    llvm::ARM::VLD3LNq16_UPD,\n    // clang-format on\n};\n\nconstexpr size_t READ_48_SIZE = sizeof(READ_48) / sizeof(unsigned);\n\nconstexpr unsigned READ_64[] = {\n    // clang-format off\n    llvm::ARM::LDAEXD,\n    llvm::ARM::LDRD,\n    llvm::ARM::LDRD_POST,\n    llvm::ARM::LDRD_PRE,\n    llvm::ARM::LDREXD,\n    llvm::ARM::VLD1d16,\n    llvm::ARM::VLD1d16wb_fixed,\n    llvm::ARM::VLD1d16wb_register,\n    llvm::ARM::VLD1d32,\n    llvm::ARM::VLD1d32wb_fixed,\n    llvm::ARM::VLD1d32wb_register,\n    llvm::ARM::VLD1d64,\n    llvm::ARM::VLD1d64wb_fixed,\n    llvm::ARM::VLD1d64wb_register,\n    llvm::ARM::VLD1d8,\n    llvm::ARM::VLD1d8wb_fixed,\n    llvm::ARM::VLD1d8wb_register,\n    llvm::ARM::VLD2DUPd32,\n    llvm::ARM::VLD2DUPd32wb_fixed,\n    llvm::ARM::VLD2DUPd32wb_register,\n    llvm::ARM::VLD2DUPd32x2,\n    llvm::ARM::VLD2DUPd32x2wb_fixed,\n    llvm::ARM::VLD2DUPd32x2wb_register,\n    llvm::ARM::VLD2LNd32,\n    llvm::ARM::VLD2LNd32_UPD,\n    llvm::ARM::VLD2LNq32,\n    llvm::ARM::VLD2LNq32_UPD,\n    llvm::ARM::VLD4DUPd16,\n    llvm::ARM::VLD4DUPd16_UPD,\n    llvm::ARM::VLD4DUPq16,\n    llvm::ARM::VLD4DUPq16_UPD,\n    llvm::ARM::VLD4LNd16,\n    llvm::ARM::VLD4LNd16_UPD,\n    llvm::ARM::VLD4LNq16,\n    llvm::ARM::VLD4LNq16_UPD,\n    llvm::ARM::VLDRD,\n    llvm::ARM::t2LDAEXD,\n    llvm::ARM::t2LDRD_POST,\n    llvm::ARM::t2LDRD_PRE,\n    llvm::ARM::t2LDRDi8,\n    llvm::ARM::t2LDREXD,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_SIZE = sizeof(READ_64) / sizeof(unsigned);\n\nconstexpr unsigned READ_96[] = {\n    // clang-format off\n    llvm::ARM::VLD3DUPd32,\n    llvm::ARM::VLD3DUPd32_UPD,\n    llvm::ARM::VLD3DUPq32,\n    llvm::ARM::VLD3DUPq32_UPD,\n    llvm::ARM::VLD3LNd32,\n    llvm::ARM::VLD3LNd32_UPD,\n    llvm::ARM::VLD3LNq32,\n    llvm::ARM::VLD3LNq32_UPD,\n    // clang-format on\n};\n\nconstexpr size_t READ_96_SIZE = sizeof(READ_96) / sizeof(unsigned);\n\nconstexpr unsigned READ_128[] = {\n    // clang-format off\n    llvm::ARM::VLD1q16,\n    llvm::ARM::VLD1q16wb_fixed,\n    llvm::ARM::VLD1q16wb_register,\n    llvm::ARM::VLD1q32,\n    llvm::ARM::VLD1q32wb_fixed,\n    llvm::ARM::VLD1q32wb_register,\n    llvm::ARM::VLD1q64,\n    llvm::ARM::VLD1q64wb_fixed,\n    llvm::ARM::VLD1q64wb_register,\n    llvm::ARM::VLD1q8,\n    llvm::ARM::VLD1q8wb_fixed,\n    llvm::ARM::VLD1q8wb_register,\n    llvm::ARM::VLD2b16,\n    llvm::ARM::VLD2b16wb_fixed,\n    llvm::ARM::VLD2b16wb_register,\n    llvm::ARM::VLD2b32,\n    llvm::ARM::VLD2b32wb_fixed,\n    llvm::ARM::VLD2b32wb_register,\n    llvm::ARM::VLD2b8,\n    llvm::ARM::VLD2b8wb_fixed,\n    llvm::ARM::VLD2b8wb_register,\n    llvm::ARM::VLD2d16,\n    llvm::ARM::VLD2d16wb_fixed,\n    llvm::ARM::VLD2d16wb_register,\n    llvm::ARM::VLD2d32,\n    llvm::ARM::VLD2d32wb_fixed,\n    llvm::ARM::VLD2d32wb_register,\n    llvm::ARM::VLD2d8,\n    llvm::ARM::VLD2d8wb_fixed,\n    llvm::ARM::VLD2d8wb_register,\n    llvm::ARM::VLD4DUPd32,\n    llvm::ARM::VLD4DUPd32_UPD,\n    llvm::ARM::VLD4DUPq32,\n    llvm::ARM::VLD4DUPq32_UPD,\n    llvm::ARM::VLD4LNd32,\n    llvm::ARM::VLD4LNd32_UPD,\n    llvm::ARM::VLD4LNq32,\n    llvm::ARM::VLD4LNq32_UPD,\n    // clang-format on\n};\n\nconstexpr size_t READ_128_SIZE = sizeof(READ_128) / sizeof(unsigned);\n\nconstexpr unsigned READ_192[] = {\n    // clang-format off\n    llvm::ARM::VLD1d16T,\n    llvm::ARM::VLD1d16Twb_fixed,\n    llvm::ARM::VLD1d16Twb_register,\n    llvm::ARM::VLD1d32T,\n    llvm::ARM::VLD1d32Twb_fixed,\n    llvm::ARM::VLD1d32Twb_register,\n    llvm::ARM::VLD1d64T,\n    llvm::ARM::VLD1d64Twb_fixed,\n    llvm::ARM::VLD1d64Twb_register,\n    llvm::ARM::VLD1d8T,\n    llvm::ARM::VLD1d8Twb_fixed,\n    llvm::ARM::VLD1d8Twb_register,\n    llvm::ARM::VLD3d16,\n    llvm::ARM::VLD3d16_UPD,\n    llvm::ARM::VLD3d32,\n    llvm::ARM::VLD3d32_UPD,\n    llvm::ARM::VLD3d8,\n    llvm::ARM::VLD3d8_UPD,\n    llvm::ARM::VLD3q16,\n    llvm::ARM::VLD3q16_UPD,\n    llvm::ARM::VLD3q32,\n    llvm::ARM::VLD3q32_UPD,\n    llvm::ARM::VLD3q8,\n    llvm::ARM::VLD3q8_UPD,\n    // clang-format on\n};\n\nconstexpr size_t READ_192_SIZE = sizeof(READ_192) / sizeof(unsigned);\n\nconstexpr unsigned READ_256[] = {\n    // clang-format off\n    llvm::ARM::VLD1d16Q,\n    llvm::ARM::VLD1d16Qwb_fixed,\n    llvm::ARM::VLD1d16Qwb_register,\n    llvm::ARM::VLD1d32Q,\n    llvm::ARM::VLD1d32Qwb_fixed,\n    llvm::ARM::VLD1d32Qwb_register,\n    llvm::ARM::VLD1d64Q,\n    llvm::ARM::VLD1d64Qwb_fixed,\n    llvm::ARM::VLD1d64Qwb_register,\n    llvm::ARM::VLD1d8Q,\n    llvm::ARM::VLD1d8Qwb_fixed,\n    llvm::ARM::VLD1d8Qwb_register,\n    llvm::ARM::VLD2q16,\n    llvm::ARM::VLD2q16wb_fixed,\n    llvm::ARM::VLD2q16wb_register,\n    llvm::ARM::VLD2q32,\n    llvm::ARM::VLD2q32wb_fixed,\n    llvm::ARM::VLD2q32wb_register,\n    llvm::ARM::VLD2q8,\n    llvm::ARM::VLD2q8wb_fixed,\n    llvm::ARM::VLD2q8wb_register,\n    llvm::ARM::VLD4d16,\n    llvm::ARM::VLD4d16_UPD,\n    llvm::ARM::VLD4d32,\n    llvm::ARM::VLD4d32_UPD,\n    llvm::ARM::VLD4d8,\n    llvm::ARM::VLD4d8_UPD,\n    llvm::ARM::VLD4q16,\n    llvm::ARM::VLD4q16_UPD,\n    llvm::ARM::VLD4q32,\n    llvm::ARM::VLD4q32_UPD,\n    llvm::ARM::VLD4q8,\n    llvm::ARM::VLD4q8_UPD,\n    // clang-format on\n};\n\nconstexpr size_t READ_256_SIZE = sizeof(READ_256) / sizeof(unsigned);\n\n// instruction with read of a multiple of 4 bytes\nconstexpr unsigned READ_32_DYN[] = {\n    // clang-format off\n    llvm::ARM::LDMDA,\n    llvm::ARM::LDMDA_UPD,\n    llvm::ARM::LDMDB,\n    llvm::ARM::LDMDB_UPD,\n    llvm::ARM::LDMIA,\n    llvm::ARM::LDMIA_UPD,\n    llvm::ARM::LDMIB,\n    llvm::ARM::LDMIB_UPD,\n    llvm::ARM::VLDMSDB_UPD,\n    llvm::ARM::VLDMSIA,\n    llvm::ARM::VLDMSIA_UPD,\n    llvm::ARM::t2LDMDB,\n    llvm::ARM::t2LDMDB_UPD,\n    llvm::ARM::t2LDMIA,\n    llvm::ARM::t2LDMIA_UPD,\n    llvm::ARM::tLDMIA,\n    llvm::ARM::tPOP,\n    // clang-format on\n};\n\nconstexpr size_t READ_32_DYN_SIZE = sizeof(READ_32_DYN) / sizeof(unsigned);\n\nconstexpr unsigned READ_64_DYN[] = {\n    // clang-format off\n    llvm::ARM::VLDMDDB_UPD,\n    llvm::ARM::VLDMDIA,\n    llvm::ARM::VLDMDIA_UPD,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_DYN_SIZE = sizeof(READ_64_DYN) / sizeof(unsigned);\n\nconstexpr unsigned UNSUPPORTED_READ[] = {\n    // clang-format off\n    llvm::ARM::LDC2L_OFFSET,\n    llvm::ARM::LDC2_OFFSET,\n    llvm::ARM::LDCL_OFFSET,\n    llvm::ARM::LDC_OFFSET,\n    llvm::ARM::MVE_VLD20_16,\n    llvm::ARM::MVE_VLD20_16_wb,\n    llvm::ARM::MVE_VLD20_32,\n    llvm::ARM::MVE_VLD20_32_wb,\n    llvm::ARM::MVE_VLD20_8,\n    llvm::ARM::MVE_VLD20_8_wb,\n    llvm::ARM::MVE_VLD21_16,\n    llvm::ARM::MVE_VLD21_16_wb,\n    llvm::ARM::MVE_VLD21_32,\n    llvm::ARM::MVE_VLD21_32_wb,\n    llvm::ARM::MVE_VLD21_8,\n    llvm::ARM::MVE_VLD21_8_wb,\n    llvm::ARM::MVE_VLD40_16,\n    llvm::ARM::MVE_VLD40_16_wb,\n    llvm::ARM::MVE_VLD40_32,\n    llvm::ARM::MVE_VLD40_32_wb,\n    llvm::ARM::MVE_VLD40_8,\n    llvm::ARM::MVE_VLD40_8_wb,\n    llvm::ARM::MVE_VLD41_16,\n    llvm::ARM::MVE_VLD41_16_wb,\n    llvm::ARM::MVE_VLD41_32,\n    llvm::ARM::MVE_VLD41_32_wb,\n    llvm::ARM::MVE_VLD41_8,\n    llvm::ARM::MVE_VLD41_8_wb,\n    llvm::ARM::MVE_VLD42_16,\n    llvm::ARM::MVE_VLD42_16_wb,\n    llvm::ARM::MVE_VLD42_32,\n    llvm::ARM::MVE_VLD42_32_wb,\n    llvm::ARM::MVE_VLD42_8,\n    llvm::ARM::MVE_VLD42_8_wb,\n    llvm::ARM::MVE_VLD43_16,\n    llvm::ARM::MVE_VLD43_16_wb,\n    llvm::ARM::MVE_VLD43_32,\n    llvm::ARM::MVE_VLD43_32_wb,\n    llvm::ARM::MVE_VLD43_8,\n    llvm::ARM::MVE_VLD43_8_wb,\n    llvm::ARM::MVE_VLDRBS16,\n    llvm::ARM::MVE_VLDRBS16_post,\n    llvm::ARM::MVE_VLDRBS16_pre,\n    llvm::ARM::MVE_VLDRBS16_rq,\n    llvm::ARM::MVE_VLDRBS32,\n    llvm::ARM::MVE_VLDRBS32_post,\n    llvm::ARM::MVE_VLDRBS32_pre,\n    llvm::ARM::MVE_VLDRBS32_rq,\n    llvm::ARM::MVE_VLDRBU16,\n    llvm::ARM::MVE_VLDRBU16_post,\n    llvm::ARM::MVE_VLDRBU16_pre,\n    llvm::ARM::MVE_VLDRBU16_rq,\n    llvm::ARM::MVE_VLDRBU32,\n    llvm::ARM::MVE_VLDRBU32_post,\n    llvm::ARM::MVE_VLDRBU32_pre,\n    llvm::ARM::MVE_VLDRBU32_rq,\n    llvm::ARM::MVE_VLDRBU8,\n    llvm::ARM::MVE_VLDRBU8_post,\n    llvm::ARM::MVE_VLDRBU8_pre,\n    llvm::ARM::MVE_VLDRBU8_rq,\n    llvm::ARM::MVE_VLDRDU64_qi,\n    llvm::ARM::MVE_VLDRDU64_qi_pre,\n    llvm::ARM::MVE_VLDRDU64_rq,\n    llvm::ARM::MVE_VLDRDU64_rq_u,\n    llvm::ARM::MVE_VLDRHS32,\n    llvm::ARM::MVE_VLDRHS32_post,\n    llvm::ARM::MVE_VLDRHS32_pre,\n    llvm::ARM::MVE_VLDRHS32_rq,\n    llvm::ARM::MVE_VLDRHS32_rq_u,\n    llvm::ARM::MVE_VLDRHU16,\n    llvm::ARM::MVE_VLDRHU16_post,\n    llvm::ARM::MVE_VLDRHU16_pre,\n    llvm::ARM::MVE_VLDRHU16_rq,\n    llvm::ARM::MVE_VLDRHU16_rq_u,\n    llvm::ARM::MVE_VLDRHU32,\n    llvm::ARM::MVE_VLDRHU32_post,\n    llvm::ARM::MVE_VLDRHU32_pre,\n    llvm::ARM::MVE_VLDRHU32_rq,\n    llvm::ARM::MVE_VLDRHU32_rq_u,\n    llvm::ARM::MVE_VLDRWU32,\n    llvm::ARM::MVE_VLDRWU32_post,\n    llvm::ARM::MVE_VLDRWU32_pre,\n    llvm::ARM::MVE_VLDRWU32_qi,\n    llvm::ARM::MVE_VLDRWU32_qi_pre,\n    llvm::ARM::MVE_VLDRWU32_rq,\n    llvm::ARM::MVE_VLDRWU32_rq_u,\n    llvm::ARM::t2LDC2L_OFFSET,\n    llvm::ARM::t2LDC2_OFFSET,\n    llvm::ARM::t2LDCL_OFFSET,\n    llvm::ARM::t2LDC_OFFSET,\n    // clang-format on\n};\n\nconstexpr size_t UNSUPPORTED_READ_SIZE =\n    sizeof(UNSUPPORTED_READ) / sizeof(unsigned);\n\n// Write Instructions\n// ==================\n\nconstexpr unsigned WRITE_8[] = {\n    // clang-format off\n    llvm::ARM::STLB,\n    llvm::ARM::STLEXB,\n    llvm::ARM::STRBT_POST_IMM,\n    llvm::ARM::STRBT_POST_REG,\n    llvm::ARM::STRB_POST_IMM,\n    llvm::ARM::STRB_POST_REG,\n    llvm::ARM::STRB_PRE_IMM,\n    llvm::ARM::STRB_PRE_REG,\n    llvm::ARM::STRBi12,\n    llvm::ARM::STRBrs,\n    llvm::ARM::STREXB,\n    llvm::ARM::SWPB,\n    llvm::ARM::VST1LNd8,\n    llvm::ARM::VST1LNd8_UPD,\n    llvm::ARM::t2STLB,\n    llvm::ARM::t2STLEXB,\n    llvm::ARM::t2STRBT,\n    llvm::ARM::t2STRB_POST,\n    llvm::ARM::t2STRB_PRE,\n    llvm::ARM::t2STRBi12,\n    llvm::ARM::t2STRBi8,\n    llvm::ARM::t2STRBs,\n    llvm::ARM::t2STREXB,\n    llvm::ARM::tSTRBi,\n    llvm::ARM::tSTRBr,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_8_SIZE = sizeof(WRITE_8) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_16[] = {\n    // clang-format off\n    llvm::ARM::STLEXH,\n    llvm::ARM::STLH,\n    llvm::ARM::STREXH,\n    llvm::ARM::STRH,\n    llvm::ARM::STRH_POST,\n    llvm::ARM::STRH_PRE,\n    llvm::ARM::VST1LNd16,\n    llvm::ARM::VST1LNd16_UPD,\n    llvm::ARM::VST2LNd8,\n    llvm::ARM::VST2LNd8_UPD,\n    llvm::ARM::VSTRH,\n    llvm::ARM::t2STLEXH,\n    llvm::ARM::t2STLEXH,\n    llvm::ARM::t2STLH,\n    llvm::ARM::t2STLH,\n    llvm::ARM::t2STREXH,\n    llvm::ARM::t2STRH_POST,\n    llvm::ARM::t2STRH_PRE,\n    llvm::ARM::t2STRHi12,\n    llvm::ARM::t2STRHi8,\n    llvm::ARM::t2STRHs,\n    llvm::ARM::tSTRHi,\n    llvm::ARM::tSTRHr,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_16_SIZE = sizeof(WRITE_16) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_24[] = {\n    // clang-format off\n    llvm::ARM::VST3LNd8,\n    llvm::ARM::VST3LNd8_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_24_SIZE = sizeof(WRITE_24) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_32[] = {\n    // clang-format off\n    llvm::ARM::STL,\n    llvm::ARM::STLEX,\n    llvm::ARM::STREX,\n    llvm::ARM::STRT_POST_IMM,\n    llvm::ARM::STRT_POST_REG,\n    llvm::ARM::STR_POST_IMM,\n    llvm::ARM::STR_POST_REG,\n    llvm::ARM::STR_PRE_IMM,\n    llvm::ARM::STR_PRE_REG,\n    llvm::ARM::STRi12,\n    llvm::ARM::STRrs,\n    llvm::ARM::SWP,\n    llvm::ARM::VST1LNd32,\n    llvm::ARM::VST1LNd32_UPD,\n    llvm::ARM::VST2LNd16,\n    llvm::ARM::VST2LNd16_UPD,\n    llvm::ARM::VST2LNq16,\n    llvm::ARM::VST2LNq16_UPD,\n    llvm::ARM::VST4LNd8,\n    llvm::ARM::VST4LNd8_UPD,\n    llvm::ARM::VSTRS,\n    llvm::ARM::VSTR_FPCXTNS_off,\n    llvm::ARM::VSTR_FPCXTNS_post,\n    llvm::ARM::VSTR_FPCXTNS_pre,\n    llvm::ARM::VSTR_FPCXTS_off,\n    llvm::ARM::VSTR_FPCXTS_post,\n    llvm::ARM::VSTR_FPCXTS_pre,\n    llvm::ARM::VSTR_FPSCR_NZCVQC_off,\n    llvm::ARM::VSTR_FPSCR_NZCVQC_post,\n    llvm::ARM::VSTR_FPSCR_NZCVQC_pre,\n    llvm::ARM::VSTR_FPSCR_off,\n    llvm::ARM::VSTR_FPSCR_post,\n    llvm::ARM::VSTR_FPSCR_pre,\n    llvm::ARM::VSTR_P0_off,\n    llvm::ARM::VSTR_P0_post,\n    llvm::ARM::VSTR_P0_pre,\n    llvm::ARM::VSTR_VPR_off,\n    llvm::ARM::VSTR_VPR_post,\n    llvm::ARM::VSTR_VPR_pre,\n    llvm::ARM::t2STL,\n    llvm::ARM::t2STLEX,\n    llvm::ARM::t2STREX,\n    llvm::ARM::t2STRT,\n    llvm::ARM::t2STR_POST,\n    llvm::ARM::t2STR_PRE,\n    llvm::ARM::t2STRi12,\n    llvm::ARM::t2STRi8,\n    llvm::ARM::t2STRs,\n    llvm::ARM::tSTRi,\n    llvm::ARM::tSTRr,\n    llvm::ARM::tSTRspi,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_32_SIZE = sizeof(WRITE_32) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_48[] = {\n    // clang-format off\n    llvm::ARM::VST3LNd16,\n    llvm::ARM::VST3LNd16_UPD,\n    llvm::ARM::VST3LNq16,\n    llvm::ARM::VST3LNq16_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_48_SIZE = sizeof(WRITE_48) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64[] = {\n    // clang-format off\n    llvm::ARM::STLEXD,\n    llvm::ARM::STRD,\n    llvm::ARM::STRD_POST,\n    llvm::ARM::STRD_PRE,\n    llvm::ARM::STREXD,\n    llvm::ARM::VST1d16,\n    llvm::ARM::VST1d16wb_fixed,\n    llvm::ARM::VST1d16wb_register,\n    llvm::ARM::VST1d32,\n    llvm::ARM::VST1d32wb_fixed,\n    llvm::ARM::VST1d32wb_register,\n    llvm::ARM::VST1d64,\n    llvm::ARM::VST1d64wb_fixed,\n    llvm::ARM::VST1d64wb_register,\n    llvm::ARM::VST1d8,\n    llvm::ARM::VST1d8wb_fixed,\n    llvm::ARM::VST1d8wb_register,\n    llvm::ARM::VST2LNd32,\n    llvm::ARM::VST2LNd32_UPD,\n    llvm::ARM::VST2LNq32,\n    llvm::ARM::VST2LNq32_UPD,\n    llvm::ARM::VST4LNd16,\n    llvm::ARM::VST4LNd16_UPD,\n    llvm::ARM::VST4LNq16,\n    llvm::ARM::VST4LNq16_UPD,\n    llvm::ARM::VSTRD,\n    llvm::ARM::t2STLEXD,\n    llvm::ARM::t2STRD_POST,\n    llvm::ARM::t2STRD_PRE,\n    llvm::ARM::t2STRDi8,\n    llvm::ARM::t2STREXD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_SIZE = sizeof(WRITE_64) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_96[] = {\n    // clang-format off\n    llvm::ARM::VST3LNd32,\n    llvm::ARM::VST3LNd32_UPD,\n    llvm::ARM::VST3LNq32,\n    llvm::ARM::VST3LNq32_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_96_SIZE = sizeof(WRITE_96) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_128[] = {\n    // clang-format off\n    llvm::ARM::VST1q16,\n    llvm::ARM::VST1q16wb_fixed,\n    llvm::ARM::VST1q16wb_register,\n    llvm::ARM::VST1q32,\n    llvm::ARM::VST1q32wb_fixed,\n    llvm::ARM::VST1q32wb_register,\n    llvm::ARM::VST1q64,\n    llvm::ARM::VST1q64wb_fixed,\n    llvm::ARM::VST1q64wb_register,\n    llvm::ARM::VST1q8,\n    llvm::ARM::VST1q8wb_fixed,\n    llvm::ARM::VST1q8wb_register,\n    llvm::ARM::VST2b16,\n    llvm::ARM::VST2b16wb_fixed,\n    llvm::ARM::VST2b16wb_register,\n    llvm::ARM::VST2b32,\n    llvm::ARM::VST2b32wb_fixed,\n    llvm::ARM::VST2b32wb_register,\n    llvm::ARM::VST2b8,\n    llvm::ARM::VST2b8wb_fixed,\n    llvm::ARM::VST2b8wb_register,\n    llvm::ARM::VST2d16,\n    llvm::ARM::VST2d16wb_fixed,\n    llvm::ARM::VST2d16wb_register,\n    llvm::ARM::VST2d32,\n    llvm::ARM::VST2d32wb_fixed,\n    llvm::ARM::VST2d32wb_register,\n    llvm::ARM::VST2d8,\n    llvm::ARM::VST2d8wb_fixed,\n    llvm::ARM::VST2d8wb_register,\n    llvm::ARM::VST4LNd32,\n    llvm::ARM::VST4LNd32_UPD,\n    llvm::ARM::VST4LNq32,\n    llvm::ARM::VST4LNq32_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_128_SIZE = sizeof(WRITE_128) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_192[] = {\n    // clang-format off\n    llvm::ARM::VST1d16T,\n    llvm::ARM::VST1d16Twb_fixed,\n    llvm::ARM::VST1d16Twb_register,\n    llvm::ARM::VST1d32T,\n    llvm::ARM::VST1d32Twb_fixed,\n    llvm::ARM::VST1d32Twb_register,\n    llvm::ARM::VST1d64T,\n    llvm::ARM::VST1d64Twb_fixed,\n    llvm::ARM::VST1d64Twb_register,\n    llvm::ARM::VST1d8T,\n    llvm::ARM::VST1d8Twb_fixed,\n    llvm::ARM::VST1d8Twb_register,\n    llvm::ARM::VST3d16,\n    llvm::ARM::VST3d16_UPD,\n    llvm::ARM::VST3d32,\n    llvm::ARM::VST3d32_UPD,\n    llvm::ARM::VST3d8,\n    llvm::ARM::VST3d8_UPD,\n    llvm::ARM::VST3q16,\n    llvm::ARM::VST3q16_UPD,\n    llvm::ARM::VST3q32,\n    llvm::ARM::VST3q32_UPD,\n    llvm::ARM::VST3q8,\n    llvm::ARM::VST3q8_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_192_SIZE = sizeof(WRITE_192) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_256[] = {\n    // clang-format off\n    llvm::ARM::VST1d16Q,\n    llvm::ARM::VST1d16Qwb_fixed,\n    llvm::ARM::VST1d16Qwb_register,\n    llvm::ARM::VST1d32Q,\n    llvm::ARM::VST1d32Qwb_fixed,\n    llvm::ARM::VST1d32Qwb_register,\n    llvm::ARM::VST1d64Q,\n    llvm::ARM::VST1d64Qwb_fixed,\n    llvm::ARM::VST1d64Qwb_register,\n    llvm::ARM::VST1d8Q,\n    llvm::ARM::VST1d8Qwb_fixed,\n    llvm::ARM::VST1d8Qwb_register,\n    llvm::ARM::VST2q16,\n    llvm::ARM::VST2q16wb_fixed,\n    llvm::ARM::VST2q16wb_register,\n    llvm::ARM::VST2q32,\n    llvm::ARM::VST2q32wb_fixed,\n    llvm::ARM::VST2q32wb_register,\n    llvm::ARM::VST2q8,\n    llvm::ARM::VST2q8wb_fixed,\n    llvm::ARM::VST2q8wb_register,\n    llvm::ARM::VST4d16,\n    llvm::ARM::VST4d16_UPD,\n    llvm::ARM::VST4d32,\n    llvm::ARM::VST4d32_UPD,\n    llvm::ARM::VST4d8,\n    llvm::ARM::VST4d8_UPD,\n    llvm::ARM::VST4q16,\n    llvm::ARM::VST4q16_UPD,\n    llvm::ARM::VST4q32,\n    llvm::ARM::VST4q32_UPD,\n    llvm::ARM::VST4q8,\n    llvm::ARM::VST4q8_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_256_SIZE = sizeof(WRITE_256) / sizeof(unsigned);\n\n// instruction with write of a multiple of 4 bytes\nconstexpr unsigned WRITE_32_DYN[] = {\n    // clang-format off\n    llvm::ARM::STMDA,\n    llvm::ARM::STMDA_UPD,\n    llvm::ARM::STMDB,\n    llvm::ARM::STMDB_UPD,\n    llvm::ARM::STMIA,\n    llvm::ARM::STMIA_UPD,\n    llvm::ARM::STMIB,\n    llvm::ARM::STMIB_UPD,\n    llvm::ARM::VSTMSDB_UPD,\n    llvm::ARM::VSTMSIA,\n    llvm::ARM::VSTMSIA_UPD,\n    llvm::ARM::t2STMDB,\n    llvm::ARM::t2STMDB_UPD,\n    llvm::ARM::t2STMIA,\n    llvm::ARM::t2STMIA_UPD,\n    llvm::ARM::tPUSH,\n    llvm::ARM::tSTMIA_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_32_DYN_SIZE = sizeof(WRITE_32_DYN) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64_DYN[] = {\n    // clang-format off\n    llvm::ARM::VSTMDDB_UPD,\n    llvm::ARM::VSTMDIA,\n    llvm::ARM::VSTMDIA_UPD,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_DYN_SIZE = sizeof(WRITE_64_DYN) / sizeof(unsigned);\n\nconstexpr unsigned UNSUPPORTED_WRITE[] = {\n    // clang-format off\n    llvm::ARM::MVE_VST20_16,\n    llvm::ARM::MVE_VST20_16_wb,\n    llvm::ARM::MVE_VST20_32,\n    llvm::ARM::MVE_VST20_32_wb,\n    llvm::ARM::MVE_VST20_8,\n    llvm::ARM::MVE_VST20_8_wb,\n    llvm::ARM::MVE_VST21_16,\n    llvm::ARM::MVE_VST21_16_wb,\n    llvm::ARM::MVE_VST21_32,\n    llvm::ARM::MVE_VST21_32_wb,\n    llvm::ARM::MVE_VST21_8,\n    llvm::ARM::MVE_VST21_8_wb,\n    llvm::ARM::MVE_VST40_16,\n    llvm::ARM::MVE_VST40_16_wb,\n    llvm::ARM::MVE_VST40_32,\n    llvm::ARM::MVE_VST40_32_wb,\n    llvm::ARM::MVE_VST40_8,\n    llvm::ARM::MVE_VST40_8_wb,\n    llvm::ARM::MVE_VST41_16,\n    llvm::ARM::MVE_VST41_16_wb,\n    llvm::ARM::MVE_VST41_32,\n    llvm::ARM::MVE_VST41_32_wb,\n    llvm::ARM::MVE_VST41_8,\n    llvm::ARM::MVE_VST41_8_wb,\n    llvm::ARM::MVE_VST42_16,\n    llvm::ARM::MVE_VST42_16_wb,\n    llvm::ARM::MVE_VST42_32,\n    llvm::ARM::MVE_VST42_32_wb,\n    llvm::ARM::MVE_VST42_8,\n    llvm::ARM::MVE_VST42_8_wb,\n    llvm::ARM::MVE_VST43_16,\n    llvm::ARM::MVE_VST43_16_wb,\n    llvm::ARM::MVE_VST43_32,\n    llvm::ARM::MVE_VST43_32_wb,\n    llvm::ARM::MVE_VST43_8,\n    llvm::ARM::MVE_VST43_8_wb,\n    llvm::ARM::MVE_VSTRB16,\n    llvm::ARM::MVE_VSTRB16_post,\n    llvm::ARM::MVE_VSTRB16_pre,\n    llvm::ARM::MVE_VSTRB16_rq,\n    llvm::ARM::MVE_VSTRB32,\n    llvm::ARM::MVE_VSTRB32_post,\n    llvm::ARM::MVE_VSTRB32_pre,\n    llvm::ARM::MVE_VSTRB32_rq,\n    llvm::ARM::MVE_VSTRB8_rq,\n    llvm::ARM::MVE_VSTRBU8,\n    llvm::ARM::MVE_VSTRBU8_post,\n    llvm::ARM::MVE_VSTRBU8_pre,\n    llvm::ARM::MVE_VSTRD64_qi,\n    llvm::ARM::MVE_VSTRD64_qi_pre,\n    llvm::ARM::MVE_VSTRD64_rq,\n    llvm::ARM::MVE_VSTRD64_rq_u,\n    llvm::ARM::MVE_VSTRH16_rq,\n    llvm::ARM::MVE_VSTRH16_rq_u,\n    llvm::ARM::MVE_VSTRH32,\n    llvm::ARM::MVE_VSTRH32_post,\n    llvm::ARM::MVE_VSTRH32_pre,\n    llvm::ARM::MVE_VSTRH32_rq,\n    llvm::ARM::MVE_VSTRH32_rq_u,\n    llvm::ARM::MVE_VSTRHU16,\n    llvm::ARM::MVE_VSTRHU16_post,\n    llvm::ARM::MVE_VSTRHU16_pre,\n    llvm::ARM::MVE_VSTRW32_qi,\n    llvm::ARM::MVE_VSTRW32_qi_pre,\n    llvm::ARM::MVE_VSTRW32_rq,\n    llvm::ARM::MVE_VSTRW32_rq_u,\n    llvm::ARM::MVE_VSTRWU32,\n    llvm::ARM::MVE_VSTRWU32_post,\n    llvm::ARM::MVE_VSTRWU32_pre,\n    llvm::ARM::STC2L_OFFSET,\n    llvm::ARM::STC2_OFFSET,\n    llvm::ARM::STCL_OFFSET,\n    llvm::ARM::STC_OFFSET,\n    llvm::ARM::t2STC2L_OFFSET,\n    llvm::ARM::t2STC2_OFFSET,\n    llvm::ARM::t2STCL_OFFSET,\n    llvm::ARM::t2STC_OFFSET,\n    // clang-format on\n};\n\nconstexpr size_t UNSUPPORTED_WRITE_SIZE =\n    sizeof(UNSUPPORTED_WRITE) / sizeof(unsigned);\n\nconstexpr unsigned TWO_BYTES_ENCODE[] = {\n    // clang-format off\n    llvm::ARM::t2IT,\n    llvm::ARM::t2SETPAN,\n    llvm::ARM::tADC,\n    llvm::ARM::tADDhirr,\n    llvm::ARM::tADDi3,\n    llvm::ARM::tADDi8,\n    llvm::ARM::tADDrSP,\n    llvm::ARM::tADDrSPi,\n    llvm::ARM::tADDrr,\n    llvm::ARM::tADDspi,\n    llvm::ARM::tADDspr,\n    llvm::ARM::tADR,\n    llvm::ARM::tAND,\n    llvm::ARM::tASRri,\n    llvm::ARM::tASRrr,\n    llvm::ARM::tB,\n    llvm::ARM::tBIC,\n    llvm::ARM::tBKPT,\n    llvm::ARM::tBLXNSr,\n    llvm::ARM::tBLXr,\n    llvm::ARM::tBX,\n    llvm::ARM::tBXNS,\n    llvm::ARM::tBcc,\n    llvm::ARM::tCBNZ,\n    llvm::ARM::tCBZ,\n    llvm::ARM::tCMNz,\n    llvm::ARM::tCMPhir,\n    llvm::ARM::tCMPi8,\n    llvm::ARM::tCMPr,\n    llvm::ARM::tCPS,\n    llvm::ARM::tEOR,\n    llvm::ARM::tHINT,\n    llvm::ARM::tHLT,\n    llvm::ARM::tLDMIA,\n    llvm::ARM::tLDRBi,\n    llvm::ARM::tLDRBr,\n    llvm::ARM::tLDRHi,\n    llvm::ARM::tLDRHr,\n    llvm::ARM::tLDRSB,\n    llvm::ARM::tLDRSH,\n    llvm::ARM::tLDRi,\n    llvm::ARM::tLDRpci,\n    llvm::ARM::tLDRr,\n    llvm::ARM::tLDRspi,\n    llvm::ARM::tLSLri,\n    llvm::ARM::tLSLrr,\n    llvm::ARM::tLSRri,\n    llvm::ARM::tLSRrr,\n    llvm::ARM::tMOVSr,\n    llvm::ARM::tMOVi8,\n    llvm::ARM::tMOVr,\n    llvm::ARM::tMUL,\n    llvm::ARM::tMVN,\n    llvm::ARM::tORR,\n    llvm::ARM::tPICADD,\n    llvm::ARM::tPOP,\n    llvm::ARM::tPUSH,\n    llvm::ARM::tREV,\n    llvm::ARM::tREV16,\n    llvm::ARM::tREVSH,\n    llvm::ARM::tROR,\n    llvm::ARM::tRSB,\n    llvm::ARM::tSBC,\n    llvm::ARM::tSETEND,\n    llvm::ARM::tSTMIA_UPD,\n    llvm::ARM::tSTRBi,\n    llvm::ARM::tSTRBr,\n    llvm::ARM::tSTRHi,\n    llvm::ARM::tSTRHr,\n    llvm::ARM::tSTRi,\n    llvm::ARM::tSTRr,\n    llvm::ARM::tSTRspi,\n    llvm::ARM::tSUBi3,\n    llvm::ARM::tSUBi8,\n    llvm::ARM::tSUBrr,\n    llvm::ARM::tSUBspi,\n    llvm::ARM::tSVC,\n    llvm::ARM::tSXTB,\n    llvm::ARM::tSXTH,\n    llvm::ARM::tTRAP,\n    llvm::ARM::tTST,\n    llvm::ARM::tUDF,\n    llvm::ARM::tUXTB,\n    llvm::ARM::tUXTH,\n    llvm::ARM::t__brkdiv0,\n    // clang-format on\n};\n\nconstexpr size_t TWO_BYTES_ENCODE_SIZE =\n    sizeof(TWO_BYTES_ENCODE) / sizeof(unsigned);\n\n/* Highest 16 bits are the write access, lowest 16 bits are the read access.\n *\n * ----------------------------\n * | 0x1f                     |\n * ----------------------------\n * | 1 bit is 16 bit encoding |\n * ----------------------------\n *\n * -----------------------------------------------------------------------------------------\n * | 0x1e                                WRITE ACCESS 0x10 |\n * -----------------------------------------------------------------------------------------\n * | 3 bits unused | 1 bit unsupported | 1 bit dynamic size | 10 bits unsigned\n * access size |\n * -----------------------------------------------------------------------------------------\n *\n * -----------------------------------------------------------------------------------------\n * | 0xf                                 READ ACCESS 0x0 |\n * -----------------------------------------------------------------------------------------\n * | 4 bits unused | 1 bit unsupported | 1 bit dynamic size | 10 bits unsigned\n * access size |\n * -----------------------------------------------------------------------------------------\n */\n\nconstexpr uint32_t WRITE_POSITION = 16;\nconstexpr uint32_t READ(uint32_t s) { return s & 0x3ff; }\nconstexpr uint32_t WRITE(uint32_t s) { return (s & 0x3ff) << WRITE_POSITION; }\nconstexpr uint32_t DYN_BIT_READ = 0x400;\nconstexpr uint32_t DYN_BIT_WRITE = DYN_BIT_READ << WRITE_POSITION;\nconstexpr uint32_t UNSUPPORTED_BIT_READ = 0x800;\nconstexpr uint32_t UNSUPPORTED_BIT_WRITE = UNSUPPORTED_BIT_READ\n                                           << WRITE_POSITION;\nconstexpr uint32_t TWO_BYTES_ENCODING = 0x80000000;\n\nconstexpr uint32_t GET_READ_SIZE(uint32_t v) { return v & 0x3ff; }\nconstexpr uint32_t GET_WRITE_SIZE(uint32_t v) {\n  return (v >> WRITE_POSITION) & 0x3ff;\n}\nconstexpr bool IS_READ_DYN(uint32_t v) {\n  return (v & DYN_BIT_READ) == DYN_BIT_READ;\n}\nconstexpr bool IS_WRITE_DYN(uint32_t v) {\n  return (v & DYN_BIT_WRITE) == DYN_BIT_WRITE;\n}\nconstexpr bool IS_UNSUPPORTED_READ(uint32_t v) {\n  return (v & UNSUPPORTED_BIT_READ) == UNSUPPORTED_BIT_READ;\n}\nconstexpr bool IS_UNSUPPORTED_WRITE(uint32_t v) {\n  return (v & UNSUPPORTED_BIT_WRITE) == UNSUPPORTED_BIT_WRITE;\n}\nconstexpr bool IS_TWO_BYTES_ENCODING(uint32_t v) {\n  return (v & TWO_BYTES_ENCODING) == TWO_BYTES_ENCODING;\n}\n\nstruct MemAccessArray {\n  uint32_t arr[llvm::ARM::INSTRUCTION_LIST_END] = {0};\n\n  constexpr inline void _initMemAccessRead(const unsigned buff[],\n                                           const size_t buffSize, uint32_t len,\n                                           bool dyn = false) {\n    for (size_t i = 0; i < buffSize; i++) {\n      arr[buff[i]] |= READ(len);\n      if (dyn) {\n        arr[buff[i]] |= DYN_BIT_READ;\n      }\n    }\n  }\n\n  constexpr inline void _initMemAccessWrite(const unsigned buff[],\n                                            const size_t buffSize, uint32_t len,\n                                            bool dyn = false) {\n    for (size_t i = 0; i < buffSize; i++) {\n      arr[buff[i]] |= WRITE(len);\n      if (dyn) {\n        arr[buff[i]] |= DYN_BIT_WRITE;\n      }\n    }\n  }\n\n  constexpr inline void _initMemUnsupported(const unsigned buff[],\n                                            const size_t buffSize, bool read) {\n    for (size_t i = 0; i < buffSize; i++) {\n      if (read) {\n        arr[buff[i]] |= UNSUPPORTED_BIT_READ;\n      } else {\n        arr[buff[i]] |= UNSUPPORTED_BIT_WRITE;\n      }\n    }\n  }\n\n  constexpr inline void _initTwoBytesEncode(const unsigned buff[],\n                                            const size_t buffSize) {\n    for (size_t i = 0; i < buffSize; i++) {\n      arr[buff[i]] |= TWO_BYTES_ENCODING;\n    }\n  }\n\n  constexpr MemAccessArray() {\n    // read\n    _initMemAccessRead(READ_8, READ_8_SIZE, 1);\n    _initMemAccessRead(READ_16, READ_16_SIZE, 2);\n    _initMemAccessRead(READ_24, READ_24_SIZE, 3);\n    _initMemAccessRead(READ_32, READ_32_SIZE, 4);\n    _initMemAccessRead(READ_48, READ_48_SIZE, 6);\n    _initMemAccessRead(READ_64, READ_64_SIZE, 8);\n    _initMemAccessRead(READ_96, READ_96_SIZE, 12);\n    _initMemAccessRead(READ_128, READ_128_SIZE, 16);\n    _initMemAccessRead(READ_192, READ_192_SIZE, 24);\n    _initMemAccessRead(READ_256, READ_256_SIZE, 32);\n    _initMemAccessRead(READ_32_DYN, READ_32_DYN_SIZE, 4, true);\n    _initMemAccessRead(READ_64_DYN, READ_64_DYN_SIZE, 8, true);\n    _initMemUnsupported(UNSUPPORTED_READ, UNSUPPORTED_READ_SIZE, true);\n    // write\n    _initMemAccessWrite(WRITE_8, WRITE_8_SIZE, 1);\n    _initMemAccessWrite(WRITE_16, WRITE_16_SIZE, 2);\n    _initMemAccessWrite(WRITE_24, WRITE_24_SIZE, 3);\n    _initMemAccessWrite(WRITE_32, WRITE_32_SIZE, 4);\n    _initMemAccessWrite(WRITE_48, WRITE_48_SIZE, 6);\n    _initMemAccessWrite(WRITE_64, WRITE_64_SIZE, 8);\n    _initMemAccessWrite(WRITE_96, WRITE_96_SIZE, 12);\n    _initMemAccessWrite(WRITE_128, WRITE_128_SIZE, 16);\n    _initMemAccessWrite(WRITE_192, WRITE_192_SIZE, 24);\n    _initMemAccessWrite(WRITE_256, WRITE_256_SIZE, 32);\n    _initMemAccessWrite(WRITE_32_DYN, WRITE_32_DYN_SIZE, 4, true);\n    _initMemAccessWrite(WRITE_64_DYN, WRITE_64_DYN_SIZE, 8, true);\n    _initMemUnsupported(UNSUPPORTED_WRITE, UNSUPPORTED_WRITE_SIZE, false);\n    // 16 bits encoding\n    _initTwoBytesEncode(TWO_BYTES_ENCODE, TWO_BYTES_ENCODE_SIZE);\n  }\n\n#if CHECK_INSTINFO_TABLE\n  void check_table(const unsigned buff[], const size_t buffSize, uint32_t value,\n                   uint32_t mask) const {\n    for (size_t i = 0; i < buffSize; i++) {\n      unsigned instID = buff[i];\n      if ((arr[instID] & mask) != value) {\n        fprintf(stderr,\n                \"[MemAccessArray::check_table], opcode %d, mask %x, expected \"\n                \"%x, found %x\\n\",\n                instID, mask, value, (arr[instID] & mask));\n        abort();\n      }\n    }\n  }\n\n  int check() const {\n    // read\n    check_table(READ_8, READ_8_SIZE, READ(1), 0xfff);\n    check_table(READ_16, READ_16_SIZE, READ(2), 0xfff);\n    check_table(READ_24, READ_24_SIZE, READ(3), 0xfff);\n    check_table(READ_32, READ_32_SIZE, READ(4), 0xfff);\n    check_table(READ_48, READ_48_SIZE, READ(6), 0xfff);\n    check_table(READ_64, READ_64_SIZE, READ(8), 0xfff);\n    check_table(READ_96, READ_96_SIZE, READ(12), 0xfff);\n    check_table(READ_128, READ_128_SIZE, READ(16), 0xfff);\n    check_table(READ_192, READ_192_SIZE, READ(24), 0xfff);\n    check_table(READ_256, READ_256_SIZE, READ(32), 0xfff);\n    check_table(READ_32_DYN, READ_32_DYN_SIZE, READ(4) | DYN_BIT_READ, 0xfff);\n    check_table(READ_64_DYN, READ_64_DYN_SIZE, READ(8) | DYN_BIT_READ, 0xfff);\n    check_table(UNSUPPORTED_READ, UNSUPPORTED_READ_SIZE, UNSUPPORTED_BIT_READ,\n                0xfff);\n    // write\n    check_table(WRITE_8, WRITE_8_SIZE, WRITE(1), 0xfff << WRITE_POSITION);\n    check_table(WRITE_16, WRITE_16_SIZE, WRITE(2), 0xfff << WRITE_POSITION);\n    check_table(WRITE_24, WRITE_24_SIZE, WRITE(3), 0xfff << WRITE_POSITION);\n    check_table(WRITE_32, WRITE_32_SIZE, WRITE(4), 0xfff << WRITE_POSITION);\n    check_table(WRITE_48, WRITE_48_SIZE, WRITE(6), 0xfff << WRITE_POSITION);\n    check_table(WRITE_64, WRITE_64_SIZE, WRITE(8), 0xfff << WRITE_POSITION);\n    check_table(WRITE_96, WRITE_96_SIZE, WRITE(12), 0xfff << WRITE_POSITION);\n    check_table(WRITE_128, WRITE_128_SIZE, WRITE(16), 0xfff << WRITE_POSITION);\n    check_table(WRITE_192, WRITE_192_SIZE, WRITE(24), 0xfff << WRITE_POSITION);\n    check_table(WRITE_256, WRITE_256_SIZE, WRITE(32), 0xfff << WRITE_POSITION);\n    check_table(WRITE_32_DYN, WRITE_32_DYN_SIZE, WRITE(4) | DYN_BIT_WRITE,\n                0xfff << WRITE_POSITION);\n    check_table(WRITE_64_DYN, WRITE_64_DYN_SIZE, WRITE(8) | DYN_BIT_WRITE,\n                0xfff << WRITE_POSITION);\n    check_table(UNSUPPORTED_WRITE, UNSUPPORTED_WRITE_SIZE,\n                UNSUPPORTED_BIT_WRITE, 0xfff << WRITE_POSITION);\n    return 0;\n  }\n#endif\n\n  inline uint32_t get(size_t op) const {\n    if (op < llvm::ARM::INSTRUCTION_LIST_END) {\n      return arr[op];\n    }\n\n    QBDI_ERROR(\"No opcode {}\", op);\n    return 0;\n  }\n};\nconstexpr MemAccessArray memAccessCache;\n\n#if CHECK_INSTINFO_TABLE\nint __check_debug = memAccessCache.check();\n#endif\n\n} // anonymous namespace\n\nunsigned getReadSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  unsigned readSize = GET_READ_SIZE(memAccessCache.get(inst.getOpcode()));\n  if (IS_READ_DYN(memAccessCache.get(inst.getOpcode()))) {\n    const llvm::MCInstrDesc &desc = llvmcpu.getMCII().get(inst.getOpcode());\n    switch (inst.getOpcode()) {\n      default:\n        break;\n      case llvm::ARM::LDMDA:\n      case llvm::ARM::LDMDA_UPD:\n      case llvm::ARM::LDMDB:\n      case llvm::ARM::LDMDB_UPD:\n      case llvm::ARM::LDMIA:\n      case llvm::ARM::LDMIA_UPD:\n      case llvm::ARM::LDMIB:\n      case llvm::ARM::LDMIB_UPD:\n      case llvm::ARM::VLDMDDB_UPD:\n      case llvm::ARM::VLDMDIA:\n      case llvm::ARM::VLDMDIA_UPD:\n      case llvm::ARM::VLDMSDB_UPD:\n      case llvm::ARM::VLDMSIA:\n      case llvm::ARM::VLDMSIA_UPD:\n      case llvm::ARM::t2LDMDB:\n      case llvm::ARM::t2LDMDB_UPD:\n      case llvm::ARM::t2LDMIA:\n      case llvm::ARM::t2LDMIA_UPD:\n      case llvm::ARM::tLDMIA:\n      case llvm::ARM::tPOP: {\n        // get number of variadic operand\n        unsigned nbVarArgs = desc.getNumOperands();\n        unsigned nbArgs = inst.getNumOperands();\n        if (nbVarArgs <= nbArgs) {\n          readSize = readSize * (1 + nbArgs - nbVarArgs);\n        }\n        break;\n      }\n    }\n  }\n  return readSize;\n}\n\nunsigned getWriteSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  unsigned writeSize = GET_WRITE_SIZE(memAccessCache.get(inst.getOpcode()));\n  if (IS_WRITE_DYN(memAccessCache.get(inst.getOpcode()))) {\n    const llvm::MCInstrDesc &desc = llvmcpu.getMCII().get(inst.getOpcode());\n    switch (inst.getOpcode()) {\n      default:\n        break;\n      case llvm::ARM::STMDA:\n      case llvm::ARM::STMDA_UPD:\n      case llvm::ARM::STMDB:\n      case llvm::ARM::STMDB_UPD:\n      case llvm::ARM::STMIA:\n      case llvm::ARM::STMIA_UPD:\n      case llvm::ARM::STMIB:\n      case llvm::ARM::STMIB_UPD:\n      case llvm::ARM::VSTMDDB_UPD:\n      case llvm::ARM::VSTMDIA:\n      case llvm::ARM::VSTMDIA_UPD:\n      case llvm::ARM::VSTMSDB_UPD:\n      case llvm::ARM::VSTMSIA:\n      case llvm::ARM::VSTMSIA_UPD:\n      case llvm::ARM::t2STMDB:\n      case llvm::ARM::t2STMDB_UPD:\n      case llvm::ARM::t2STMIA:\n      case llvm::ARM::t2STMIA_UPD:\n      case llvm::ARM::tPUSH:\n      case llvm::ARM::tSTMIA_UPD: {\n        // get number of variadic operand\n        unsigned nbVarArgs = desc.getNumOperands();\n        unsigned nbArgs = inst.getNumOperands();\n        if (nbVarArgs <= nbArgs) {\n          writeSize = writeSize * (1 + nbArgs - nbVarArgs);\n        }\n        break;\n      }\n    }\n  }\n  return writeSize;\n}\n\nunsigned getInstSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n\n  if (IS_TWO_BYTES_ENCODING(memAccessCache.get(inst.getOpcode()))) {\n    return 2;\n  } else {\n    return 4;\n  }\n}\n\nunsigned getImmediateSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  // TODO check llvm\n  return 2;\n}\n\nbool unsupportedRead(const llvm::MCInst &inst) {\n  return IS_UNSUPPORTED_READ(memAccessCache.get(inst.getOpcode()));\n}\n\nbool unsupportedWrite(const llvm::MCInst &inst) {\n  return IS_UNSUPPORTED_WRITE(memAccessCache.get(inst.getOpcode()));\n}\n\nunsigned getCondition(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n  const llvm::MCInstrDesc &desc = MCII.get(inst.getOpcode());\n\n  unsigned int predicateFirstOffset = 0;\n  bool found = false;\n  for (unsigned int opn = 0; opn < desc.getNumOperands(); opn++) {\n    const llvm::MCOperandInfo &opInfo = desc.operands()[opn];\n    if (opInfo.isPredicate()) {\n      found = true;\n      predicateFirstOffset = opn;\n      break;\n    }\n  }\n\n  if (not found) {\n    return llvm::ARMCC::AL;\n  }\n\n  QBDI_REQUIRE_ABORT(predicateFirstOffset + 1 < desc.getNumOperands(),\n                     \"Invalid operand id {} ({})\", predicateFirstOffset + 1,\n                     llvmcpu.getInstOpcodeName(inst));\n\n  const llvm::MCOperandInfo &opInfoReg =\n      desc.operands()[predicateFirstOffset + 1];\n\n  QBDI_REQUIRE_ABORT(opInfoReg.isPredicate(),\n                     \"Unexpected operandInfo type {} ({})\",\n                     predicateFirstOffset + 1, llvmcpu.getInstOpcodeName(inst));\n  QBDI_REQUIRE_ABORT(predicateFirstOffset + 1 < inst.getNumOperands(),\n                     \"Invalid operand id {} ({})\", predicateFirstOffset + 1,\n                     llvmcpu.getInstOpcodeName(inst));\n  QBDI_REQUIRE_ABORT(inst.getOperand(predicateFirstOffset).isImm(),\n                     \"Unexpected operand type {} ({})\", predicateFirstOffset,\n                     llvmcpu.getInstOpcodeName(inst));\n  QBDI_REQUIRE_ABORT(inst.getOperand(predicateFirstOffset + 1).isReg(),\n                     \"Unexpected operand type {} ({})\",\n                     predicateFirstOffset + 1, llvmcpu.getInstOpcodeName(inst));\n\n  RegLLVM r = inst.getOperand(predicateFirstOffset + 1).getReg();\n  unsigned cond = inst.getOperand(predicateFirstOffset).getImm();\n\n  if (cond == llvm::ARMCC::AL) {\n    QBDI_REQUIRE_ABORT(\n        r == llvm::ARM::NoRegister, \"Unexpected operand value {} ({})\",\n        llvmcpu.getRegisterName(r), llvmcpu.getInstOpcodeName(inst));\n  } else {\n    QBDI_REQUIRE_ABORT(r == llvm::ARM::CPSR, \"Unexpected operand value {} ({})\",\n                       llvmcpu.getRegisterName(r),\n                       llvmcpu.getInstOpcodeName(inst));\n    QBDI_REQUIRE_ABORT(cond < llvm::ARMCC::AL, \"Unexpected condition {} ({})\",\n                       cond, llvmcpu.getInstOpcodeName(inst));\n  }\n  return cond;\n}\n\nbool variadicOpsIsWrite(const llvm::MCInst &inst) {\n\n  switch (inst.getOpcode()) {\n    default:\n      return false;\n    case llvm::ARM::LDMDA:\n    case llvm::ARM::LDMDA_UPD:\n    case llvm::ARM::LDMDB:\n    case llvm::ARM::LDMDB_UPD:\n    case llvm::ARM::LDMIA:\n    case llvm::ARM::LDMIA_UPD:\n    case llvm::ARM::LDMIB:\n    case llvm::ARM::LDMIB_UPD:\n    case llvm::ARM::VLDMDDB_UPD:\n    case llvm::ARM::VLDMDIA:\n    case llvm::ARM::VLDMDIA_UPD:\n    case llvm::ARM::VLDMSDB_UPD:\n    case llvm::ARM::VLDMSIA:\n    case llvm::ARM::VLDMSIA_UPD:\n    case llvm::ARM::t2LDMDB:\n    case llvm::ARM::t2LDMDB_UPD:\n    case llvm::ARM::t2LDMIA:\n    case llvm::ARM::t2LDMIA_UPD:\n    case llvm::ARM::tLDMIA:\n    case llvm::ARM::tPOP:\n      return true;\n  }\n}\n\nsword getFixedOperandValue(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                           unsigned index, int64_t value) {\n  switch (inst.getOpcode()) {\n    default:\n      break;\n    case llvm::ARM::LDRBT_POST_IMM:\n    case llvm::ARM::LDRB_POST_IMM:\n    case llvm::ARM::LDRT_POST_IMM:\n    case llvm::ARM::LDR_POST_IMM:\n    case llvm::ARM::STRBT_POST_IMM:\n    case llvm::ARM::STRB_POST_IMM:\n    case llvm::ARM::STRT_POST_IMM:\n    case llvm::ARM::STR_POST_IMM: {\n      if (index != 4)\n        break;\n      // see lib/Target/ARM/MCTargetDesc/ARMAddressingModes.h getAM2Opc()\n      // remove the encoding, as shift and index mode is not needed\n      if (llvm::ARM_AM::getAM2Op(value) == llvm::ARM_AM::sub) {\n        return -llvm::ARM_AM::getAM2Offset(value);\n      } else {\n        return llvm::ARM_AM::getAM2Offset(value);\n      }\n    }\n    case llvm::ARM::t2IT:\n      if (index != 1)\n        return QBDI::InstructionAnalysis::ConditionLLVM2QBDI(value);\n      break;\n    case llvm::ARM::VLDRH:\n    case llvm::ARM::VSTRH:\n    case llvm::ARM::tLDRHi:\n    case llvm::ARM::tSTRHi:\n      if (index == 2)\n        return value << 1;\n      break;\n    case llvm::ARM::VLDRD:\n    case llvm::ARM::VLDRS:\n    case llvm::ARM::VSTRD:\n    case llvm::ARM::VSTRS:\n    case llvm::ARM::tLDRi:\n    case llvm::ARM::tLDRspi:\n    case llvm::ARM::tSTRi:\n    case llvm::ARM::tSTRspi:\n    case llvm::ARM::t2LDREX:\n      if (index == 2)\n        return value << 2;\n      break;\n    case llvm::ARM::t2STREX:\n      if (index == 3)\n        return value << 2;\n      break;\n  }\n  return static_cast<rword>(value);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/InstInfo_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTINFO_ARM_H\n#define INSTINFO_ARM_H\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\nclass LLVMCPU;\n\nunsigned getCondition(const llvm::MCInst &inst, const LLVMCPU &llvmcpu);\n}; // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/InstMetadata_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTMETADATA_ARM_H\n#define INSTMETADATA_ARM_H\n\nnamespace QBDI {\n\nstruct InstMetadataArch {\n  // condition associated with the instruction\n  uint8_t cond;\n  // position in an ITBlock (0 = !InITBlock(), 1 = InITBlock() &&\n  // LastInITBlock(), >1 = InITBlock() && !LastInITBlock())\n  uint8_t posITblock;\n};\n\n} // namespace QBDI\n\n#endif // INSTMETADATA_ARM_H\n"
  },
  {
    "path": "src/Patch/ARM/InstrRules_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"ExecBlock/Context.h\"\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"Patch/ARM/PatchGenerator_ARM.h\"\n#include \"Patch/ARM/RelocatableInst_ARM.h\"\n#include \"Patch/InstrRules.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\n/* Genreate a series of RelocatableInst which when appended to an\n * instrumentation code trigger a break to host. It receive in argument a\n * temporary reg which will be used for computations then finally restored.\n */\nRelocatableInst::UniquePtrVec getBreakToHost(Reg temp, const Patch &patch,\n                                             bool restore) {\n\n  QBDI_REQUIRE_ABORT(restore, \"ARM don't have a temporary register {}\", patch);\n\n  // compute offset address\n  unsigned int patchSize = RelativeAddress(temp, 0xff).getSize(*patch.llvmcpu);\n\n  RelocatableInst::UniquePtrVec tmpVec;\n  append(tmpVec, SaveReg(temp, Offset(offsetof(Context, hostState.selector)))\n                     .genReloc(*patch.llvmcpu));\n  append(tmpVec, LoadReg(temp, Offset(temp)).genReloc(*patch.llvmcpu));\n  append(tmpVec, JmpEpilogue().genReloc(*patch.llvmcpu));\n\n  patchSize += getUniquePtrVecSize(tmpVec, *patch.llvmcpu);\n  if (patch.metadata.cpuMode == CPUMode::Thumb) {\n    patchSize += 1;\n  }\n\n  RelocatableInst::UniquePtrVec breakToHost;\n\n  // set temp to the address after JmpEpilogue\n  breakToHost.push_back(RelativeAddress::unique(temp, patchSize));\n\n  // set address in hostState.selector\n  // Restore the temporary register\n  // JumpEpilogue\n  append(breakToHost, std::move(tmpVec));\n\n  // add target when callback return CONTINUE\n  append(breakToHost, TargetPrologue().genReloc(patch));\n\n  return breakToHost;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/Layer2_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"Patch/ARM/RelocatableInst_ARM.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"Target/ARM/ARMSubtarget.h\"\n#include \"Target/ARM/MCTargetDesc/ARMAddressingModes.h\"\n\nnamespace QBDI {\n\nstatic inline unsigned int getCondReg(unsigned cond) {\n  if (cond == llvm::ARMCC::AL) {\n    return llvm::ARM::NoRegister;\n  } else {\n    return llvm::ARM::CPSR;\n  }\n}\n\nstatic inline void ldmstmCommon(llvm::MCInst &inst, RegLLVM base,\n                                unsigned int regMask, unsigned cond) {\n  QBDI_REQUIRE_ABORT(regMask != 0, \"Empty register list\");\n  QBDI_REQUIRE_ABORT((regMask >> 16) == 0,\n                     \"Unsupported register in list mask: 0x{:x}\", regMask);\n\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  for (int i = 0; i < 16; i++) {\n    if ((regMask & (1 << i)) != 0) {\n      switch (i) {\n        default:\n          inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::R0 + i));\n          break;\n        case 13:\n          inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::SP));\n          break;\n        case 14:\n          inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::LR));\n          break;\n        case 15:\n          inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::PC));\n          break;\n      }\n    }\n  }\n}\n\n// utils\n\nbool armExpandCompatible(rword imm) {\n  return llvm::ARM_AM::getSOImmVal(imm) != -1;\n}\n\nbool thumbExpandCompatible(rword imm) {\n  return llvm::ARM_AM::getT2SOImmValSplatVal(imm) != -1;\n}\n\nbool t2moviCompatible(rword imm) {\n  if (imm < 256) {\n    return true;\n  }\n  for (int i = 0; i < 24; i++) {\n    rword mask = (1 << (i)) - 1;\n    if ((imm & mask) != 0) {\n      return false;\n    }\n    if ((imm >> i) < 256) {\n      return true;\n    }\n  }\n  return false;\n}\n\n// LoadInst 4\n\nllvm::MCInst popr1(RegLLVM reg, unsigned cond) {\n  return ldrPost(reg, llvm::ARM::SP, 4, cond);\n}\n\nllvm::MCInst ldri12(RegLLVM reg, RegLLVM base, sword offset) {\n  return ldri12(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-4096 < offset and offset < 4096,\n                     \"offset not in the range [-4095, 4095] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::LDRi12);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst ldrPost(RegLLVM dst, RegLLVM src, sword offset) {\n  return ldrPost(dst, src, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrPost(RegLLVM dst, RegLLVM src, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-4096 < offset and offset < 4096,\n                     \"offset not in the range [-4095, 4095] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::LDR_POST_IMM);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldri8(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2ldri8(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldri8(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-256 < offset and offset < 256,\n                     \"offset not in the range [-255, 255] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::t2LDRi8);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2ldri12(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2ldri12(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(0 <= offset and offset < 4096,\n                     \"offset not in the range [0, 4095] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::t2LDRi12);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2ldrPost(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2ldrPost(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrPost(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-256 < offset and offset < 256,\n                     \"offset not in the range [-255, 255] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::t2LDR_POST);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2ldrPre(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2ldrPre(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrPre(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-256 < offset and offset < 256,\n                     \"offset not in the range [-255, 255] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::t2LDR_PRE);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\n// LoadInst 2\n\nllvm::MCInst ldrh(RegLLVM dst, RegLLVM src, unsigned int offset) {\n  return ldrh(dst, src, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrh(RegLLVM dst, RegLLVM src, unsigned int offset,\n                  unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::LDRH);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst ldrhPost(RegLLVM dst, RegLLVM src) {\n  return ldrhPost(dst, src, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrhPost(RegLLVM dst, RegLLVM src, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::LDRH_POST);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(2));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrh(RegLLVM dst, RegLLVM src, unsigned int offset) {\n  return t2ldrh(dst, src, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrh(RegLLVM dst, RegLLVM src, unsigned int offset,\n                    unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDRHi12);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrhPost(RegLLVM dst, RegLLVM src) {\n  return t2ldrhPost(dst, src, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrhPost(RegLLVM dst, RegLLVM src, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDRH_POST);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(2));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrhr(RegLLVM base, RegLLVM reg, RegLLVM offReg) {\n  return t2ldrhrs(base, reg, offReg, 0, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrhr(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned cond) {\n  return t2ldrhrs(base, reg, offReg, 0, cond);\n}\n\nllvm::MCInst t2ldrhrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl) {\n  return t2ldrhrs(base, reg, offReg, lsl, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrhrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl,\n                      unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(lsl <= 4, \"Invalid shift value: {}\", lsl);\n\n  inst.setOpcode(llvm::ARM::t2LDRHs);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(offReg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(lsl));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\n// LoadInst 1\n\nllvm::MCInst ldrb(RegLLVM dst, RegLLVM src, unsigned int offset) {\n  return ldrb(dst, src, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrb(RegLLVM dst, RegLLVM src, unsigned int offset,\n                  unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::LDRBi12);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst ldrbPost(RegLLVM dst, RegLLVM src) {\n  return ldrbPost(dst, src, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrbPost(RegLLVM dst, RegLLVM src, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::LDRB_POST_IMM);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrb(RegLLVM dst, RegLLVM src, unsigned int offset) {\n  return t2ldrb(dst, src, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrb(RegLLVM dst, RegLLVM src, unsigned int offset,\n                    unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDRBi12);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrbPost(RegLLVM dst, RegLLVM src) {\n  return t2ldrbPost(dst, src, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrbPost(RegLLVM dst, RegLLVM src, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDRB_POST);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrbr(RegLLVM base, RegLLVM reg, RegLLVM offReg) {\n  return t2ldrbrs(base, reg, offReg, 0, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrbr(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned cond) {\n  return t2ldrbrs(base, reg, offReg, 0, cond);\n}\n\nllvm::MCInst t2ldrbrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl) {\n  return t2ldrbrs(base, reg, offReg, lsl, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrbrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl,\n                      unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(lsl <= 4, \"Invalid shift value: {}\", lsl);\n\n  inst.setOpcode(llvm::ARM::t2LDRBs);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(offReg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(lsl));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\n// load exclusif\n\nllvm::MCInst ldrexb(RegLLVM dest, RegLLVM reg) {\n  return ldrexb(dest, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrexb(RegLLVM dest, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::LDREXB);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrexb(RegLLVM dest, RegLLVM reg) {\n  return t2ldrexb(dest, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrexb(RegLLVM dest, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDREXB);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst ldrexh(RegLLVM dest, RegLLVM reg) {\n  return ldrexh(dest, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrexh(RegLLVM dest, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::LDREXH);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrexh(RegLLVM dest, RegLLVM reg) {\n  return t2ldrexh(dest, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrexh(RegLLVM dest, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDREXH);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst ldrex(RegLLVM dest, RegLLVM reg) {\n  return ldrex(dest, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrex(RegLLVM dest, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::LDREX);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrex(RegLLVM dest, RegLLVM reg) {\n  return t2ldrex(dest, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrex(RegLLVM dest, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDREX);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(0));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst ldrexd(RegLLVM dest, RegLLVM dest2, RegLLVM reg) {\n  return ldrexd(dest, dest2, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldrexd(RegLLVM dest, RegLLVM dest2, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n  unsigned coupleReg = 0;\n\n  QBDI_REQUIRE_ABORT(dest2.getValue() - dest.getValue() == 1,\n                     \"Need consecutive register\");\n\n  switch (dest.getValue()) {\n    case llvm::ARM::R0:\n      coupleReg = llvm::ARM::R0_R1;\n      break;\n    case llvm::ARM::R2:\n      coupleReg = llvm::ARM::R2_R3;\n      break;\n    case llvm::ARM::R4:\n      coupleReg = llvm::ARM::R4_R5;\n      break;\n    case llvm::ARM::R6:\n      coupleReg = llvm::ARM::R6_R7;\n      break;\n    case llvm::ARM::R8:\n      coupleReg = llvm::ARM::R8_R9;\n      break;\n    case llvm::ARM::R10:\n      coupleReg = llvm::ARM::R10_R11;\n      break;\n    default:\n      QBDI_ABORT(\"Invalid destination register\");\n  }\n\n  inst.setOpcode(llvm::ARM::LDREXD);\n  inst.addOperand(llvm::MCOperand::createReg(coupleReg));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2ldrexd(RegLLVM dest, RegLLVM dest2, RegLLVM reg) {\n  return t2ldrexd(dest, dest2, reg, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldrexd(RegLLVM dest, RegLLVM dest2, RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2LDREXD);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dest2.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\n// load multiple\n\nllvm::MCInst ldmia(RegLLVM base, unsigned int regMask, bool wback) {\n  return ldmia(base, regMask, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst ldmia(RegLLVM base, unsigned int regMask, bool wback,\n                   unsigned cond) {\n  llvm::MCInst inst;\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::LDMIA_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::LDMIA);\n  }\n  ldmstmCommon(inst, base, regMask, cond);\n  return inst;\n}\n\nllvm::MCInst t2ldmia(RegLLVM base, unsigned int regMask, bool wback) {\n  return t2ldmia(base, regMask, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldmia(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond) {\n  llvm::MCInst inst;\n  // SP forbidden in thumb mode\n  QBDI_REQUIRE_ABORT(((regMask >> 13) & 1) == 0,\n                     \"SP forbidden in thumb mode (regMask : 0x{:x}\", regMask);\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::t2LDMIA_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::t2LDMIA);\n  }\n  ldmstmCommon(inst, base, regMask, cond);\n  return inst;\n}\n\nllvm::MCInst t2ldmdb(RegLLVM base, unsigned int regMask, bool wback) {\n  return t2ldmdb(base, regMask, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2ldmdb(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond) {\n  llvm::MCInst inst;\n  // SP forbidden in thumb mode\n  QBDI_REQUIRE_ABORT(((regMask >> 13) & 1) == 0,\n                     \"SP forbidden in thumb mode (regMask : 0x{:x}\", regMask);\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::t2LDMDB_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::t2LDMDB);\n  }\n  ldmstmCommon(inst, base, regMask, cond);\n  return inst;\n}\n\nllvm::MCInst vldmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback) {\n  return vldmia(base, reg, nreg, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst vldmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback,\n                    unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(0 < nreg and nreg <= 16,\n                     \"Invalid number of register to load: {}\", nreg);\n  QBDI_REQUIRE_ABORT(llvm::ARM::D0 <= reg.getValue() and\n                         reg.getValue() + (nreg - 1) <= llvm::ARM::D31,\n                     \"Invalid register to load (D{} ({}), {})\",\n                     reg.getValue() - llvm::ARM::D0, reg.getValue(), nreg);\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::VLDMDIA_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::VLDMDIA);\n  }\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  for (unsigned int i = 0; i < nreg; i++) {\n    inst.addOperand(llvm::MCOperand::createReg(reg.getValue() + i));\n  }\n  return inst;\n}\n\n// StoreInst\n\nllvm::MCInst pushr1(RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::STR_PRE_IMM);\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::SP));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::SP));\n  inst.addOperand(llvm::MCOperand::createImm(-4));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst stri12(RegLLVM reg, RegLLVM base, sword offset) {\n  return stri12(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst stri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-4096 < offset and offset < 4096,\n                     \"offset not in the range [-4095, 4095] ({})\", offset);\n\n  QBDI_REQUIRE_ABORT(reg != llvm::ARM::PC, \"Source register cannot be PC\");\n\n  inst.setOpcode(llvm::ARM::STRi12);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2stri8(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2stri8(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2stri8(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-256 < offset and offset < 256,\n                     \"offset not in the range [-255, 255] ({})\", offset);\n\n  // llvm allows to create t2stri8 with PC, but the instruction is marked as\n  // UNDEFINED (for base == PC) or UNPREDICTABLE (for reg == PC) in the\n  // documentation\n  QBDI_REQUIRE_ABORT(base != llvm::ARM::PC, \"Base register cannot be PC\");\n  QBDI_REQUIRE_ABORT(reg != llvm::ARM::PC, \"Source register cannot be PC\");\n\n  inst.setOpcode(llvm::ARM::t2STRi8);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2stri12(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2stri12(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2stri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(0 <= offset and offset < 4096,\n                     \"offset not in the range [0, 4095] ({})\", offset);\n\n  // llvm allows to create t2stri12 with PC, but the instruction is marked as\n  // UNDEFINED (for base == PC) or UNPREDICTABLE (for reg == PC) in the\n  // documentation\n  QBDI_REQUIRE_ABORT(base != llvm::ARM::PC, \"Base register cannot be PC\");\n  QBDI_REQUIRE_ABORT(reg != llvm::ARM::PC, \"Source register cannot be PC\");\n\n  inst.setOpcode(llvm::ARM::t2STRi12);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2strPost(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2strPost(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2strPost(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-256 < offset and offset < 256,\n                     \"offset not in the range [-255, 255] ({})\", offset);\n  QBDI_REQUIRE_ABORT(base != llvm::ARM::PC, \"Base register cannot be PC\");\n  QBDI_REQUIRE_ABORT(reg != llvm::ARM::PC, \"Source register cannot be PC\");\n\n  inst.setOpcode(llvm::ARM::t2STR_POST);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2strPre(RegLLVM reg, RegLLVM base, sword offset) {\n  return t2strPre(reg, base, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2strPre(RegLLVM reg, RegLLVM base, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-256 < offset and offset < 256,\n                     \"offset not in the range [-255, 255] ({})\", offset);\n  QBDI_REQUIRE_ABORT(base != llvm::ARM::PC, \"Base register cannot be PC\");\n  QBDI_REQUIRE_ABORT(reg != llvm::ARM::PC, \"Source register cannot be PC\");\n\n  inst.setOpcode(llvm::ARM::t2STR_PRE);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\n// store multiple\n\nllvm::MCInst stmia(RegLLVM base, unsigned int regMask, bool wback) {\n  return stmia(base, regMask, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst stmia(RegLLVM base, unsigned int regMask, bool wback,\n                   unsigned cond) {\n  llvm::MCInst inst;\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::STMIA_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::STMIA);\n  }\n  ldmstmCommon(inst, base, regMask, cond);\n  return inst;\n}\n\nllvm::MCInst t2stmia(RegLLVM base, unsigned int regMask, bool wback) {\n  return t2stmia(base, regMask, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2stmia(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond) {\n  llvm::MCInst inst;\n  // SP and PC forbidden in thumb mode\n  QBDI_REQUIRE_ABORT(((regMask >> 13) & 0x5) == 0,\n                     \"SP and PC forbidden in thumb mode (regMask : 0x{:x}\",\n                     regMask);\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::t2STMIA_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::t2STMIA);\n  }\n  ldmstmCommon(inst, base, regMask, cond);\n  return inst;\n}\n\nllvm::MCInst t2stmdb(RegLLVM base, unsigned int regMask, bool wback) {\n  return t2stmdb(base, regMask, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2stmdb(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(((regMask >> 13) & 0x5) == 0,\n                     \"SP and PC forbidden in thumb mode (regMask : 0x{:x}\",\n                     regMask);\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::t2STMDB_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::t2STMDB);\n  }\n  ldmstmCommon(inst, base, regMask, cond);\n  return inst;\n}\n\nllvm::MCInst vstmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback) {\n  return vstmia(base, reg, nreg, wback, llvm::ARMCC::AL);\n}\n\nllvm::MCInst vstmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback,\n                    unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(0 < nreg and nreg <= 16,\n                     \"Invalid number of register to store: {}\", nreg);\n  QBDI_REQUIRE_ABORT(llvm::ARM::D0 <= reg.getValue() and\n                         reg.getValue() + (nreg - 1) <= llvm::ARM::D31,\n                     \"Invalid register to store (D{} ({}), {})\",\n                     reg.getValue() - llvm::ARM::D0, reg.getValue(), nreg);\n\n  if (wback) {\n    inst.setOpcode(llvm::ARM::VSTMDIA_UPD);\n    inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  } else {\n    inst.setOpcode(llvm::ARM::VSTMDIA);\n  }\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  for (unsigned int i = 0; i < nreg; i++) {\n    inst.addOperand(llvm::MCOperand::createReg(reg.getValue() + i));\n  }\n  return inst;\n}\n\n// mov register - register\n\nllvm::MCInst movr(RegLLVM dst, RegLLVM src) {\n  return movr(dst, src, llvm::ARMCC::AL);\n}\n\nllvm::MCInst movr(RegLLVM dst, RegLLVM src, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::MOVr_TC);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n\n  return inst;\n}\n\nllvm::MCInst tmovr(RegLLVM dst, RegLLVM src) {\n  return tmovr(dst, src, llvm::ARMCC::AL);\n}\n\nllvm::MCInst tmovr(RegLLVM dst, RegLLVM src, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::tMOVr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\n// mov Immediate\n\nllvm::MCInst movi(RegLLVM dst, rword imm) {\n  return movi(dst, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst movi(RegLLVM dst, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(imm < 0x10000, \"Unsupported immediate 0x{:x}\", imm);\n\n  inst.setOpcode(llvm::ARM::MOVi16);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2movi(RegLLVM dst, rword imm) {\n  return t2movi(dst, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2movi(RegLLVM dst, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(t2moviCompatible(imm), \"Incompatible immediate 0x{:x}\",\n                     imm);\n\n  inst.setOpcode(llvm::ARM::t2MOVi);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n\n  return inst;\n}\n\n// branch\n\nllvm::MCInst branch(sword offset) { return bcc(offset, llvm::ARMCC::AL); }\n\nllvm::MCInst bcc(sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(offset % 4 == 0, \"Invalid alignemnt 0x{:x}\", offset);\n  QBDI_REQUIRE_ABORT(-(1 << 26) < offset and offset < (1 << 26),\n                     \"offset not in the range [-0x3ffffff, 0x3ffffff] (0x{:x})\",\n                     offset);\n\n  inst.setOpcode(llvm::ARM::Bcc);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst tbranch(sword offset) {\n  return tbranchIT(offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst tbranchIT(sword offset, unsigned cond) {\n  // if cond != llvm::ARMCC::AL, the instruction must be within an IT block\n  // (at the last position)\n\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(offset % 2 == 0, \"Invalid alignemnt 0x{:x}\", offset);\n  QBDI_REQUIRE_ABORT(-2048 < offset and offset < 2048,\n                     \"offset not in the range [-2047, 2047] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::tB);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst tbcc(sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(offset % 2 == 0, \"Invalid alignemnt 0x{:x}\", offset);\n  QBDI_REQUIRE_ABORT(-256 < offset and offset < 256,\n                     \"offset not in the range [-255, 255] ({})\", offset);\n  QBDI_REQUIRE_ABORT(cond != llvm::ARMCC::AL, \"Unsupported condition AL\");\n\n  inst.setOpcode(llvm::ARM::tBcc);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2branch(sword offset) {\n  return t2branchIT(offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2branchIT(sword offset, unsigned cond) {\n  // if cond != llvm::ARMCC::AL, the instruction must be within an IT block\n  // (at the last position)\n\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(offset % 2 == 0, \"Invalid alignemnt 0x{:x}\", offset);\n  QBDI_REQUIRE_ABORT(-0x1000000 < offset and offset < 0x1000000,\n                     \"offset not in the range [-0xffffff, 0xffffff] (0x{:x})\",\n                     offset);\n\n  inst.setOpcode(llvm::ARM::t2B);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2bcc(sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(offset % 2 == 0, \"Invalid alignemnt 0x{:x}\", offset);\n  QBDI_REQUIRE_ABORT(-0x100000 < offset and offset < 0x100000,\n                     \"offset not in the range [-0xfffff, 0xfffff] (0x{:x})\",\n                     offset);\n  QBDI_REQUIRE_ABORT(cond != llvm::ARMCC::AL, \"Unsupported condition AL\");\n\n  inst.setOpcode(llvm::ARM::t2Bcc);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst tbx(RegLLVM reg) { return tbx(reg, llvm::ARMCC::AL); }\n\nllvm::MCInst tbx(RegLLVM reg, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::tBX);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\n// flags mov\n\nllvm::MCInst mrs(RegLLVM dst) { return mrs(dst, llvm::ARMCC::AL); }\n\nllvm::MCInst mrs(RegLLVM dst, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::MRS);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst msr(RegLLVM dst) { return msr(dst, llvm::ARMCC::AL); }\n\nllvm::MCInst msr(RegLLVM dst, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::MSR);\n  inst.addOperand(llvm::MCOperand::createImm(/* APSR_nzcvqg */ 0xc));\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2mrs(RegLLVM dst) { return t2mrs(dst, llvm::ARMCC::AL); }\n\nllvm::MCInst t2mrs(RegLLVM dst, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2MRS_AR);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst t2msr(RegLLVM dst) { return t2msr(dst, llvm::ARMCC::AL); }\n\nllvm::MCInst t2msr(RegLLVM dst, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2MSR_AR);\n  inst.addOperand(llvm::MCOperand::createImm(/* APSR_nzcvqg */ 0xc));\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst vmrs(RegLLVM dst) { return vmrs(dst, llvm::ARMCC::AL); }\n\nllvm::MCInst vmrs(RegLLVM dst, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::VMRS);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\nllvm::MCInst vmsr(RegLLVM dst) { return vmsr(dst, llvm::ARMCC::AL); }\n\nllvm::MCInst vmsr(RegLLVM dst, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::VMSR);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\n// get relative address\n\nllvm::MCInst adr(RegLLVM reg, rword offset) {\n  return adr(reg, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst adr(RegLLVM reg, rword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(armExpandCompatible(offset), \"Incompatible offset 0x{:x}\",\n                     offset);\n\n  inst.setOpcode(llvm::ARM::ADR);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2adr(RegLLVM reg, sword offset) {\n  return t2adr(reg, offset, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2adr(RegLLVM reg, sword offset, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(-4096 < offset and offset < 4096,\n                     \"offset not in the range [-4095, 4095] ({})\", offset);\n\n  inst.setOpcode(llvm::ARM::t2ADR);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\n// addition\n\nllvm::MCInst add(RegLLVM dst, RegLLVM src, rword imm) {\n  return add(dst, src, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst add(RegLLVM dst, RegLLVM src, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(((sword)imm) >= 0, \"Invalid immediate 0x{:x}\", imm);\n  QBDI_REQUIRE_ABORT(armExpandCompatible(imm), \"Incompatible immediate 0x{:x}\",\n                     imm);\n\n  inst.setOpcode(llvm::ARM::ADDri);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::ARM_AM::getSOImmVal(imm)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst t2add(RegLLVM dst, RegLLVM src, rword imm) {\n  return t2add(dst, src, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2add(RegLLVM dst, RegLLVM src, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(((sword)imm) >= 0, \"Invalid immediate 0x{:x}\", imm);\n  QBDI_REQUIRE_ABORT(imm < 4096, \"Invalid immediate 0x{:x}\", imm);\n  QBDI_REQUIRE_ABORT(dst != llvm::ARM::SP or src == llvm::ARM::SP,\n                     \"Dest register can be SP only if it's also the source\");\n\n  inst.setOpcode(llvm::ARM::t2ADDri12);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType) {\n  return addrsi(dst, src, srcOff, shift, shiftType, llvm::ARMCC::AL);\n}\n\nllvm::MCInst addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(\n      (shiftType == llvm::ARM_AM::lsl) or (shiftType == llvm::ARM_AM::lsr) or\n          (shiftType == llvm::ARM_AM::asr) or\n          (shiftType == llvm::ARM_AM::ror) or (shiftType == llvm::ARM_AM::rrx),\n      \"Unsupported shift type {}\", shiftType);\n  QBDI_REQUIRE_ABORT(shift < (1 << 5), \"Unsupported shift: 0x{:x}\", shift);\n\n  inst.setOpcode(llvm::ARM::ADDrsi);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(srcOff.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(\n      llvm::ARM_AM::getSORegOpc((llvm::ARM_AM::ShiftOpc)shiftType, shift)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst t2addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType) {\n  return t2addrsi(dst, src, srcOff, shift, shiftType, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(\n      (shiftType == llvm::ARM_AM::lsl) or (shiftType == llvm::ARM_AM::lsr) or\n          (shiftType == llvm::ARM_AM::asr) or\n          (shiftType == llvm::ARM_AM::ror) or (shiftType == llvm::ARM_AM::rrx),\n      \"Unsupported shift type {}\", shiftType);\n  QBDI_REQUIRE_ABORT(shift < (1 << 5), \"Unsupported shift: 0x{:x}\", shift);\n\n  inst.setOpcode(llvm::ARM::t2ADDrs);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(srcOff.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(\n      llvm::ARM_AM::getSORegOpc((llvm::ARM_AM::ShiftOpc)shiftType, shift)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst addr(RegLLVM dst, RegLLVM src, RegLLVM src2) {\n  return addr(dst, src, src2, llvm::ARMCC::AL);\n}\n\nllvm::MCInst addr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::ADDrr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst t2addr(RegLLVM dst, RegLLVM src, RegLLVM src2) {\n  return t2addr(dst, src, src2, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2addr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2ADDrr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\n// substration\n\nllvm::MCInst sub(RegLLVM dst, RegLLVM src, rword imm) {\n  return sub(dst, src, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst sub(RegLLVM dst, RegLLVM src, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(((sword)imm) >= 0, \"Invalid immediate 0x{:x}\", imm);\n  QBDI_REQUIRE_ABORT(armExpandCompatible(imm), \"Incompatible immediate 0x{:x}\",\n                     imm);\n\n  inst.setOpcode(llvm::ARM::SUBri);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::ARM_AM::getSOImmVal(imm)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst t2sub(RegLLVM dst, RegLLVM src, rword imm) {\n  return t2sub(dst, src, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2sub(RegLLVM dst, RegLLVM src, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(((sword)imm) >= 0, \"Invalid immediate 0x{:x}\", imm);\n  QBDI_REQUIRE_ABORT(imm < 4096, \"Invalid immediate 0x{:x}\", imm);\n  QBDI_REQUIRE_ABORT(dst != llvm::ARM::SP or src == llvm::ARM::SP,\n                     \"Dest register can be SP only if it's also the source\");\n\n  inst.setOpcode(llvm::ARM::t2SUBri12);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType) {\n  return subrsi(dst, src, srcOff, shift, shiftType, llvm::ARMCC::AL);\n}\n\nllvm::MCInst subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(\n      (shiftType == llvm::ARM_AM::lsl) or (shiftType == llvm::ARM_AM::lsr) or\n          (shiftType == llvm::ARM_AM::asr) or\n          (shiftType == llvm::ARM_AM::ror) or (shiftType == llvm::ARM_AM::rrx),\n      \"Unsupported shift type {}\", shiftType);\n  QBDI_REQUIRE_ABORT(shift < (1 << 5), \"Unsupported shift: 0x{:x}\", shift);\n\n  inst.setOpcode(llvm::ARM::SUBrsi);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(srcOff.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(shiftType | (shift << 3)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst t2subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType) {\n  return t2subrsi(dst, src, srcOff, shift, shiftType, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(\n      (shiftType == llvm::ARM_AM::lsl) or (shiftType == llvm::ARM_AM::lsr) or\n          (shiftType == llvm::ARM_AM::asr) or\n          (shiftType == llvm::ARM_AM::ror) or (shiftType == llvm::ARM_AM::rrx),\n      \"Unsupported shift type {}\", shiftType);\n  QBDI_REQUIRE_ABORT(shift < (1 << 5), \"Unsupported shift: 0x{:x}\", shift);\n\n  inst.setOpcode(llvm::ARM::t2SUBrs);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(srcOff.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(\n      llvm::ARM_AM::getSORegOpc((llvm::ARM_AM::ShiftOpc)shiftType, shift)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst subr(RegLLVM dst, RegLLVM src, RegLLVM src2) {\n  return subr(dst, src, src2, llvm::ARMCC::AL);\n}\n\nllvm::MCInst subr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::SUBrr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst t2subr(RegLLVM dst, RegLLVM src, RegLLVM src2) {\n  return t2subr(dst, src, src2, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2subr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2SUBrr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src2.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\n// bit clear\n\nllvm::MCInst bic(RegLLVM dst, RegLLVM src, rword imm) {\n  return bic(dst, src, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst bic(RegLLVM dst, RegLLVM src, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(armExpandCompatible(imm), \"Incompatible immediate 0x{:x}\",\n                     imm);\n\n  inst.setOpcode(llvm::ARM::BICri);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::ARM_AM::getSOImmVal(imm)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n  return inst;\n}\n\nllvm::MCInst t2bic(RegLLVM dest, RegLLVM reg, rword imm) {\n  return t2bic(dest, reg, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2bic(RegLLVM dest, RegLLVM reg, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(thumbExpandCompatible(imm),\n                     \"Incompatible immediate 0x{:x}\", imm);\n\n  inst.setOpcode(llvm::ARM::t2BICri);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n\n  return inst;\n}\n\n// or\n\nllvm::MCInst orri(RegLLVM dest, RegLLVM reg, rword imm) {\n  return orri(dest, reg, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst orri(RegLLVM dest, RegLLVM reg, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(armExpandCompatible(imm), \"Incompatible immediate 0x{:x}\",\n                     imm);\n\n  inst.setOpcode(llvm::ARM::ORRri);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::ARM_AM::getSOImmVal(imm)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n\n  return inst;\n}\n\nllvm::MCInst t2orri(RegLLVM dest, RegLLVM reg, rword imm) {\n  return t2orri(dest, reg, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2orri(RegLLVM dest, RegLLVM reg, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(thumbExpandCompatible(imm),\n                     \"Incompatible immediate 0x{:x}\", imm);\n\n  inst.setOpcode(llvm::ARM::t2ORRri);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n\n  return inst;\n}\n\nllvm::MCInst orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                      unsigned int lshift) {\n  return orrshift(dest, reg, reg2, lshift, llvm::ARMCC::AL);\n}\n\nllvm::MCInst orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                      unsigned int lshift, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::ORRrsi);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg2.getValue()));\n  inst.addOperand(\n      llvm::MCOperand::createImm((lshift << 3) | llvm::ARM_AM::ShiftOpc::lsl));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  return inst;\n}\n\nllvm::MCInst t2orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                        unsigned int lshift) {\n  return t2orrshift(dest, reg, reg2, lshift, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                        unsigned int lshift, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2ORRrs);\n  inst.addOperand(llvm::MCOperand::createReg(dest.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(reg2.getValue()));\n  inst.addOperand(\n      llvm::MCOperand::createImm((lshift << 3) | llvm::ARM_AM::ShiftOpc::lsl));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  return inst;\n}\n\n// cmp\n\nllvm::MCInst cmp(RegLLVM src, rword imm) {\n  return cmp(src, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst cmp(RegLLVM src, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(armExpandCompatible(imm), \"Incompatible immediate 0x{:x}\",\n                     imm);\n\n  inst.setOpcode(llvm::ARM::CMPri);\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::ARM_AM::getSOImmVal(imm)));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  return inst;\n}\n\nllvm::MCInst t2cmp(RegLLVM reg, rword imm) {\n  return t2cmp(reg, imm, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2cmp(RegLLVM reg, rword imm, unsigned cond) {\n  llvm::MCInst inst;\n  QBDI_REQUIRE_ABORT(thumbExpandCompatible(imm),\n                     \"Incompatible immediate 0x{:x}\", imm);\n\n  inst.setOpcode(llvm::ARM::t2CMPri);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n\n  return inst;\n}\n\n// misc\n\nllvm::MCInst t2it(unsigned int cond, unsigned pred) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2IT);\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createImm(pred));\n\n  return inst;\n}\n\nllvm::MCInst nop() {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::HINT);\n  inst.addOperand(llvm::MCOperand::createImm(0));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::ARMCC::AL));\n  inst.addOperand(llvm::MCOperand::createReg(llvm::ARM::NoRegister));\n\n  return inst;\n}\n\nllvm::MCInst bkpt(unsigned int value) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::BKPT);\n  inst.addOperand(llvm::MCOperand::createImm(value));\n\n  return inst;\n}\n\nllvm::MCInst t2autg(RegLLVM reg, RegLLVM ctx, RegLLVM tag) {\n  return t2autg(reg, ctx, tag, llvm::ARMCC::AL);\n}\n\nllvm::MCInst t2autg(RegLLVM reg, RegLLVM ctx, RegLLVM tag, unsigned cond) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::ARM::t2AUTG);\n  inst.addOperand(llvm::MCOperand::createImm(cond));\n  inst.addOperand(llvm::MCOperand::createReg(getCondReg(cond)));\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(ctx.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(tag.getValue()));\n\n  return inst;\n}\n\n// High level layer 2\n\nRelocatableInst::UniquePtr Popr1(CPUMode cpuMode, Reg reg) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(popr1(reg, llvm::ARMCC::AL));\n}\n\nRelocatableInst::UniquePtr Pushr1(CPUMode cpuMode, Reg reg) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(pushr1(reg, llvm::ARMCC::AL));\n}\n\nRelocatableInst::UniquePtr Add(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                               Constant cst) {\n  if (cpuMode == CPUMode::ARM) {\n    if (((sword)cst) < 0) {\n      return NoReloc::unique(sub(dst, src, -((sword)cst)));\n    } else {\n      return NoReloc::unique(add(dst, src, cst));\n    }\n  } else {\n    if (((sword)cst) < 0) {\n      return NoReloc::unique(t2sub(dst, src, -((sword)cst)));\n    } else {\n      return NoReloc::unique(t2add(dst, src, cst));\n    }\n  }\n}\n\nRelocatableInst::UniquePtr Addr(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                RegLLVM src2) {\n  if (cpuMode == CPUMode::ARM) {\n    return NoReloc::unique(addr(dst, src, src2));\n  } else {\n    return NoReloc::unique(t2addr(dst, src, src2));\n  }\n}\n\nRelocatableInst::UniquePtr Subr(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                RegLLVM src2) {\n  if (cpuMode == CPUMode::ARM) {\n    return NoReloc::unique(subr(dst, src, src2));\n  } else {\n    return NoReloc::unique(t2subr(dst, src, src2));\n  }\n}\n\nRelocatableInst::UniquePtr Addrs(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                 RegLLVM srcOff, unsigned shift,\n                                 unsigned shiftType) {\n  if (cpuMode == CPUMode::ARM) {\n    return NoReloc::unique(addrsi(dst, src, srcOff, shift, shiftType));\n  } else {\n    return NoReloc::unique(t2addrsi(dst, src, srcOff, shift, shiftType));\n  }\n}\n\nRelocatableInst::UniquePtr Subrs(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                 RegLLVM srcOff, unsigned shift,\n                                 unsigned shiftType) {\n  if (cpuMode == CPUMode::ARM) {\n    return NoReloc::unique(subrsi(dst, src, srcOff, shift, shiftType));\n  } else {\n    return NoReloc::unique(t2subrsi(dst, src, srcOff, shift, shiftType));\n  }\n}\n\nRelocatableInst::UniquePtr LdmIA(CPUMode cpuMode, RegLLVM base,\n                                 unsigned int regMask, bool wback) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(ldmia(base, regMask, wback));\n}\n\nRelocatableInst::UniquePtr StmIA(CPUMode cpuMode, RegLLVM base,\n                                 unsigned int regMask, bool wback) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(stmia(base, regMask, wback));\n}\n\nRelocatableInst::UniquePtr VLdmIA(CPUMode cpuMode, RegLLVM base,\n                                  unsigned int reg, unsigned int nreg,\n                                  bool wback) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(vldmia(base, llvm::ARM::D0 + reg, nreg, wback));\n}\n\nRelocatableInst::UniquePtr VStmIA(CPUMode cpuMode, RegLLVM base,\n                                  unsigned int reg, unsigned int nreg,\n                                  bool wback) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(vstmia(base, llvm::ARM::D0 + reg, nreg, wback));\n}\n\nRelocatableInst::UniquePtr Mrs(CPUMode cpuMode, Reg reg) {\n  if (cpuMode == CPUMode::ARM) {\n    return NoReloc::unique(mrs(reg));\n  } else {\n    return NoReloc::unique(t2mrs(reg));\n  }\n}\n\nRelocatableInst::UniquePtr Msr(CPUMode cpuMode, Reg reg) {\n  if (cpuMode == CPUMode::ARM) {\n    return NoReloc::unique(msr(reg));\n  } else {\n    return NoReloc::unique(t2msr(reg));\n  }\n}\n\nRelocatableInst::UniquePtr Vmrs(CPUMode cpuMode, Reg reg) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(vmrs(reg));\n}\n\nRelocatableInst::UniquePtr Vmsr(CPUMode cpuMode, Reg reg) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(vmsr(reg));\n}\n\nRelocatableInst::UniquePtr Bkpt(CPUMode cpuMode, unsigned int value) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::ARM, \"Available only in ARM mode\");\n\n  return NoReloc::unique(bkpt(value));\n}\n\nRelocatableInst::UniquePtr T2it(CPUMode cpuMode, unsigned int cond,\n                                unsigned pred) {\n  QBDI_REQUIRE_ABORT(cpuMode == CPUMode::Thumb, \"Available only in Thumb mode\");\n\n  return NoReloc::unique(t2it(cond, pred));\n}\n\nRelocatableInst::UniquePtrVec Addc(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                   Constant val, RegLLVM temp) {\n  return Addc(cpuMode, dst, src, val, temp, llvm::ARMCC::AL);\n}\n\nRelocatableInst::UniquePtrVec Addc(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                   Constant val, RegLLVM temp,\n                                   unsigned int cond) {\n\n  sword sval = static_cast<sword>(val);\n  bool isNeg = (sval < 0);\n  rword absVal = isNeg ? -sval : sval;\n\n  if (cpuMode == CPUMode::ARM) {\n    QBDI_REQUIRE_ABORT(temp != src,\n                       \"Source register cannot be used as Temp register\");\n\n    // if the value is already compatible, used it without change\n    if (armExpandCompatible(absVal)) {\n      if (isNeg) {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(sub(dst, src, absVal, cond)));\n      } else {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(add(dst, src, absVal, cond)));\n      }\n    } else if ((absVal & 0xffff) == absVal and\n               armExpandCompatible(absVal & 0xff) and\n               armExpandCompatible(absVal & 0xff00)) {\n      if (isNeg) {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(sub(temp, src, absVal & 0xff, cond)),\n            NoReloc::unique(sub(dst, temp, absVal & 0xff00, cond)));\n      } else {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(add(temp, src, absVal & 0xff, cond)),\n            NoReloc::unique(add(dst, temp, absVal & 0xff00, cond)));\n      }\n    } else {\n      if (isNeg) {\n        return conv_unique<RelocatableInst>(\n            LoadImmCC::unique(temp, absVal, cond),\n            NoReloc::unique(subr(dst, src, temp, cond)));\n      } else {\n        return conv_unique<RelocatableInst>(\n            LoadImmCC::unique(temp, absVal, cond),\n            NoReloc::unique(addr(dst, src, temp, cond)));\n      }\n    }\n  } else {\n    RelocatableInst::UniquePtrVec vec;\n\n    // if the value is already compatible, used it without change\n    if (absVal < 4096) {\n      if (cond != llvm::ARMCC::AL) {\n        vec.push_back(\n            T2it(cpuMode, cond, (unsigned)llvm::ARM::PredBlockMask::T));\n      }\n      if (isNeg) {\n        vec.push_back(NoReloc::unique(t2sub(dst, src, absVal, cond)));\n      } else {\n        vec.push_back(NoReloc::unique(t2add(dst, src, absVal, cond)));\n      }\n    } else {\n      if (cond != llvm::ARMCC::AL) {\n        vec.push_back(\n            T2it(cpuMode, cond, (unsigned)llvm::ARM::PredBlockMask::TT));\n      }\n      vec.push_back(LoadImmCC::unique(temp, absVal, cond));\n      if (isNeg) {\n        vec.push_back(NoReloc::unique(t2subr(dst, src, temp, cond)));\n      } else {\n        vec.push_back(NoReloc::unique(t2addr(dst, src, temp, cond)));\n      }\n    }\n    return vec;\n  }\n}\n\nRelocatableInst::UniquePtr Cmp(CPUMode cpuMode, RegLLVM src, rword imm) {\n  if (cpuMode == CPUMode::ARM) {\n    return NoReloc::unique(cmp(src, imm));\n  } else {\n    return NoReloc::unique(t2cmp(src, imm));\n  }\n}\n\nRelocatableInst::UniquePtr Branch(CPUMode cpuMode, sword offset,\n                                  bool addBranchLen) {\n  return BranchCC(cpuMode, offset, llvm::ARMCC::AL, false, addBranchLen);\n}\n\nRelocatableInst::UniquePtr BranchCC(CPUMode cpuMode, sword offset,\n                                    unsigned cond, bool withinITBlock,\n                                    bool addBranchLen) {\n\n  QBDI_DEBUG(\"BranchCC {} offset={} cond={} addBranchLen={}\", cpuMode, offset,\n             cond, addBranchLen);\n  if (cpuMode == CPUMode::ARM) {\n    if (addBranchLen and offset > 0) {\n      offset += 4;\n    }\n    return NoReloc::unique(bcc(offset - 8, cond));\n  } else {\n    if (addBranchLen and offset > 0) {\n      offset += 2;\n    }\n    if (cond == llvm::ARMCC::AL or withinITBlock) {\n      if (-2048 < (offset - 4) and (offset - 4) < 2048) {\n        return NoReloc::unique(tbranchIT(offset - 4, cond));\n      } else {\n        if (addBranchLen and offset > 0) {\n          offset += 2;\n        }\n        return NoReloc::unique(t2branchIT(offset - 4, cond));\n      }\n    } else {\n      if (-256 < (offset - 4) and (offset - 4) < 256) {\n        return NoReloc::unique(tbcc(offset - 4, cond));\n      } else {\n        if (addBranchLen and offset > 0) {\n          offset += 2;\n        }\n        return NoReloc::unique(t2bcc(offset - 4, cond));\n      }\n    }\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/Layer2_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_LAYER2_ARM_H\n#define QBDI_LAYER2_ARM_H\n\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\n// Low level layer 2\n\n// utils\nbool armExpandCompatible(rword imm);\nbool thumbExpandCompatible(rword imm);\nbool t2moviCompatible(rword imm);\n\n// LoadInst 4\nllvm::MCInst popr1(RegLLVM reg, unsigned cond);\n\nllvm::MCInst ldri12(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst ldri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst ldrPost(RegLLVM base, RegLLVM reg, sword offset);\nllvm::MCInst ldrPost(RegLLVM base, RegLLVM reg, sword offset, unsigned cond);\nllvm::MCInst t2ldri8(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2ldri8(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst t2ldri12(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2ldri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst t2ldrPost(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2ldrPost(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst t2ldrPre(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2ldrPre(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\n\n// LoadInst 2\n\nllvm::MCInst ldrh(RegLLVM base, RegLLVM reg, unsigned int offset);\nllvm::MCInst ldrh(RegLLVM base, RegLLVM reg, unsigned int offset,\n                  unsigned cond);\nllvm::MCInst ldrhPost(RegLLVM base, RegLLVM reg);\nllvm::MCInst ldrhPost(RegLLVM base, RegLLVM reg, unsigned cond);\n\nllvm::MCInst t2ldrh(RegLLVM base, RegLLVM reg, unsigned int offset);\nllvm::MCInst t2ldrh(RegLLVM base, RegLLVM reg, unsigned int offset,\n                    unsigned cond);\nllvm::MCInst t2ldrhPost(RegLLVM base, RegLLVM reg);\nllvm::MCInst t2ldrhPost(RegLLVM base, RegLLVM reg, unsigned cond);\n\nllvm::MCInst t2ldrhr(RegLLVM base, RegLLVM reg, RegLLVM offReg);\nllvm::MCInst t2ldrhr(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned cond);\nllvm::MCInst t2ldrhrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl);\nllvm::MCInst t2ldrhrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl,\n                      unsigned cond);\n\n// LoadInst 1\n\nllvm::MCInst ldrb(RegLLVM base, RegLLVM reg, unsigned int offset);\nllvm::MCInst ldrb(RegLLVM base, RegLLVM reg, unsigned int offset,\n                  unsigned cond);\nllvm::MCInst ldrbPost(RegLLVM base, RegLLVM reg);\nllvm::MCInst ldrbPost(RegLLVM base, RegLLVM reg, unsigned cond);\n\nllvm::MCInst t2ldrb(RegLLVM base, RegLLVM reg, unsigned int offset);\nllvm::MCInst t2ldrb(RegLLVM base, RegLLVM reg, unsigned int offset,\n                    unsigned cond);\nllvm::MCInst t2ldrbPost(RegLLVM base, RegLLVM reg);\nllvm::MCInst t2ldrbPost(RegLLVM base, RegLLVM reg, unsigned cond);\n\nllvm::MCInst t2ldrbr(RegLLVM base, RegLLVM reg, RegLLVM offReg);\nllvm::MCInst t2ldrbr(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned cond);\nllvm::MCInst t2ldrbrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl);\nllvm::MCInst t2ldrbrs(RegLLVM base, RegLLVM reg, RegLLVM offReg, unsigned lsl,\n                      unsigned cond);\n\n// load exclusif\n\nllvm::MCInst ldrexb(RegLLVM base, RegLLVM reg);\nllvm::MCInst ldrexb(RegLLVM base, RegLLVM reg, unsigned cond);\nllvm::MCInst t2ldrexb(RegLLVM base, RegLLVM reg);\nllvm::MCInst t2ldrexb(RegLLVM base, RegLLVM reg, unsigned cond);\n\nllvm::MCInst ldrexh(RegLLVM base, RegLLVM reg);\nllvm::MCInst ldrexh(RegLLVM base, RegLLVM reg, unsigned cond);\nllvm::MCInst t2ldrexh(RegLLVM base, RegLLVM reg);\nllvm::MCInst t2ldrexh(RegLLVM base, RegLLVM reg, unsigned cond);\n\nllvm::MCInst ldrex(RegLLVM base, RegLLVM reg);\nllvm::MCInst ldrex(RegLLVM base, RegLLVM reg, unsigned cond);\nllvm::MCInst t2ldrex(RegLLVM base, RegLLVM reg);\nllvm::MCInst t2ldrex(RegLLVM base, RegLLVM reg, unsigned cond);\n\nllvm::MCInst ldrexd(RegLLVM base, RegLLVM base2, RegLLVM reg);\nllvm::MCInst ldrexd(RegLLVM base, RegLLVM base2, RegLLVM reg, unsigned cond);\nllvm::MCInst t2ldrexd(RegLLVM base, RegLLVM base2, RegLLVM reg);\nllvm::MCInst t2ldrexd(RegLLVM base, RegLLVM base2, RegLLVM reg, unsigned cond);\n\n// load multiple\n\nllvm::MCInst ldmia(RegLLVM base, unsigned int regMask, bool wback);\nllvm::MCInst ldmia(RegLLVM base, unsigned int regMask, bool wback,\n                   unsigned cond);\nllvm::MCInst t2ldmia(RegLLVM base, unsigned int regMask, bool wback);\nllvm::MCInst t2ldmia(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond);\nllvm::MCInst t2ldmdb(RegLLVM base, unsigned int regMask, bool wback);\nllvm::MCInst t2ldmdb(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond);\n\nllvm::MCInst vldmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback);\nllvm::MCInst vldmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback,\n                    unsigned cond);\n\n// StoreInst\nllvm::MCInst pushr1(RegLLVM reg, unsigned cond);\n\nllvm::MCInst stri12(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst stri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst t2stri8(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2stri8(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst t2stri12(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2stri12(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst t2strPost(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2strPost(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\nllvm::MCInst t2strPre(RegLLVM reg, RegLLVM base, sword offset);\nllvm::MCInst t2strPre(RegLLVM reg, RegLLVM base, sword offset, unsigned cond);\n\n// store multiple\n\nllvm::MCInst stmia(RegLLVM base, unsigned int regMask, bool wback);\nllvm::MCInst stmia(RegLLVM base, unsigned int regMask, bool wback,\n                   unsigned cond);\nllvm::MCInst t2stmia(RegLLVM base, unsigned int regMask, bool wback);\nllvm::MCInst t2stmia(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond);\nllvm::MCInst t2stmdb(RegLLVM base, unsigned int regMask, bool wback);\nllvm::MCInst t2stmdb(RegLLVM base, unsigned int regMask, bool wback,\n                     unsigned cond);\n\nllvm::MCInst vstmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback);\nllvm::MCInst vstmia(RegLLVM base, RegLLVM reg, unsigned int nreg, bool wback,\n                    unsigned cond);\n\n// mov register - register\n\nllvm::MCInst movr(RegLLVM dst, RegLLVM src);\nllvm::MCInst movr(RegLLVM dst, RegLLVM src, unsigned cond);\nllvm::MCInst tmovr(RegLLVM dst, RegLLVM src);\nllvm::MCInst tmovr(RegLLVM dst, RegLLVM src, unsigned cond);\n\n// mov Immediate\n\nllvm::MCInst movi(RegLLVM dst, rword imm);\nllvm::MCInst movi(RegLLVM dst, rword imm, unsigned cond);\n\nllvm::MCInst t2movi(RegLLVM dst, rword imm);\nllvm::MCInst t2movi(RegLLVM dst, rword imm, unsigned cond);\n\n// branch\n\nllvm::MCInst branch(sword offset);\nllvm::MCInst bcc(sword offset, unsigned cond);\nllvm::MCInst tbranch(sword offset);\nllvm::MCInst tbranchIT(sword offset, unsigned cond);\nllvm::MCInst tbcc(sword offset, unsigned cond);\nllvm::MCInst t2branch(sword offset);\nllvm::MCInst t2branchIT(sword offset, unsigned cond);\nllvm::MCInst t2bcc(sword offset, unsigned cond);\n\nllvm::MCInst tbx(RegLLVM reg);\nllvm::MCInst tbx(RegLLVM reg, unsigned cond);\n\n// flags mov\nllvm::MCInst mrs(RegLLVM dst);\nllvm::MCInst mrs(RegLLVM dst, unsigned cond);\nllvm::MCInst msr(RegLLVM dst);\nllvm::MCInst msr(RegLLVM dst, unsigned cond);\nllvm::MCInst t2mrs(RegLLVM dst);\nllvm::MCInst t2mrs(RegLLVM dst, unsigned cond);\nllvm::MCInst t2msr(RegLLVM dst);\nllvm::MCInst t2msr(RegLLVM dst, unsigned cond);\n\nllvm::MCInst vmrs(RegLLVM dst);\nllvm::MCInst vmrs(RegLLVM dst, unsigned cond);\nllvm::MCInst vmsr(RegLLVM dst);\nllvm::MCInst vmsr(RegLLVM dst, unsigned cond);\n\n// get relative address\n\nllvm::MCInst adr(RegLLVM reg, rword offset);\nllvm::MCInst adr(RegLLVM reg, rword offset, unsigned cond);\nllvm::MCInst t2adr(RegLLVM reg, sword offset);\nllvm::MCInst t2adr(RegLLVM reg, sword offset, unsigned cond);\n\n// add\n\nllvm::MCInst add(RegLLVM dst, RegLLVM src, rword imm);\nllvm::MCInst add(RegLLVM dst, RegLLVM src, rword imm, unsigned cond);\nllvm::MCInst t2add(RegLLVM dst, RegLLVM src, rword imm);\nllvm::MCInst t2add(RegLLVM dst, RegLLVM src, rword imm, unsigned cond);\n\nllvm::MCInst addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType);\nllvm::MCInst addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType, unsigned cond);\nllvm::MCInst t2addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType);\nllvm::MCInst t2addrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType, unsigned cond);\n\nllvm::MCInst addr(RegLLVM dst, RegLLVM src, RegLLVM src2);\nllvm::MCInst addr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond);\nllvm::MCInst t2addr(RegLLVM dst, RegLLVM src, RegLLVM src2);\nllvm::MCInst t2addr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond);\n\n// sub\n\nllvm::MCInst sub(RegLLVM dst, RegLLVM src, rword imm);\nllvm::MCInst sub(RegLLVM dst, RegLLVM src, rword imm, unsigned cond);\nllvm::MCInst t2sub(RegLLVM dst, RegLLVM src, rword imm);\nllvm::MCInst t2sub(RegLLVM dst, RegLLVM src, rword imm, unsigned cond);\n\nllvm::MCInst subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType);\nllvm::MCInst subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                    unsigned shiftType, unsigned cond);\nllvm::MCInst t2subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType);\nllvm::MCInst t2subrsi(RegLLVM dst, RegLLVM src, RegLLVM srcOff, unsigned shift,\n                      unsigned shiftType, unsigned cond);\n\nllvm::MCInst subr(RegLLVM dst, RegLLVM src, RegLLVM src2);\nllvm::MCInst subr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond);\nllvm::MCInst t2subr(RegLLVM dst, RegLLVM src, RegLLVM src2);\nllvm::MCInst t2subr(RegLLVM dst, RegLLVM src, RegLLVM src2, unsigned cond);\n\n// bit clear\n\nllvm::MCInst bic(RegLLVM dst, RegLLVM src, rword imm);\nllvm::MCInst bic(RegLLVM dst, RegLLVM src, rword imm, unsigned cond);\nllvm::MCInst t2bic(RegLLVM dst, RegLLVM src, rword imm);\nllvm::MCInst t2bic(RegLLVM dst, RegLLVM src, rword imm, unsigned cond);\n\n// or\n\nllvm::MCInst orri(RegLLVM dest, RegLLVM reg, rword imm);\nllvm::MCInst orri(RegLLVM dest, RegLLVM reg, rword imm, unsigned cond);\nllvm::MCInst t2orri(RegLLVM dest, RegLLVM reg, rword imm);\nllvm::MCInst t2orri(RegLLVM dest, RegLLVM reg, rword imm, unsigned cond);\n\nllvm::MCInst orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                      unsigned int lshift);\nllvm::MCInst orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                      unsigned int lshift, unsigned cond);\nllvm::MCInst t2orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                        unsigned int lshift);\nllvm::MCInst t2orrshift(RegLLVM dest, RegLLVM reg, RegLLVM reg2,\n                        unsigned int lshift, unsigned cond);\n\n// compare\n\nllvm::MCInst cmp(RegLLVM src, rword imm);\nllvm::MCInst cmp(RegLLVM src, rword imm, unsigned cond);\nllvm::MCInst t2cmp(RegLLVM src, rword imm);\nllvm::MCInst t2cmp(RegLLVM src, rword imm, unsigned cond);\n\n// misc\n\n// see Utils/ARMBaseInfo.h llvm::ARMCC::CondCodes and llvm::ARM::PredBlockMask\nllvm::MCInst t2it(unsigned int cond, unsigned pred);\nllvm::MCInst nop();\nllvm::MCInst bkpt(unsigned int value);\nllvm::MCInst t2autg(RegLLVM reg, RegLLVM ctx, RegLLVM tag);\nllvm::MCInst t2autg(RegLLVM reg, RegLLVM ctx, RegLLVM tag, unsigned cond);\n\n// High level layer 2\n\nstd::unique_ptr<RelocatableInst> Popr1(CPUMode cpuMode, Reg reg);\nstd::unique_ptr<RelocatableInst> Pushr1(CPUMode cpuMode, Reg reg);\n\nstd::unique_ptr<RelocatableInst> Add(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                     Constant cst);\nstd::unique_ptr<RelocatableInst> Addr(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                      RegLLVM src2);\nstd::unique_ptr<RelocatableInst> Subr(CPUMode cpuMode, RegLLVM dst, RegLLVM src,\n                                      RegLLVM src2);\nstd::unique_ptr<RelocatableInst> Addrs(CPUMode cpuMode, RegLLVM dst,\n                                       RegLLVM src, RegLLVM srcOff,\n                                       unsigned shift, unsigned shiftType);\nstd::unique_ptr<RelocatableInst> Subrs(CPUMode cpuMode, RegLLVM dst,\n                                       RegLLVM src, RegLLVM srcOff,\n                                       unsigned shift, unsigned shiftType);\n\nstd::unique_ptr<RelocatableInst>\nLdmIA(CPUMode cpuMode, RegLLVM base, unsigned int regMask, bool wback = false);\nstd::unique_ptr<RelocatableInst>\nStmIA(CPUMode cpuMode, RegLLVM base, unsigned int regMask, bool wback = false);\nstd::unique_ptr<RelocatableInst> VLdmIA(CPUMode cpuMode, RegLLVM base,\n                                        unsigned int reg, unsigned int nreg,\n                                        bool wback = false);\nstd::unique_ptr<RelocatableInst> VStmIA(CPUMode cpuMode, RegLLVM base,\n                                        unsigned int reg, unsigned int nreg,\n                                        bool wback = false);\n\nstd::unique_ptr<RelocatableInst> Mrs(CPUMode cpuMode, Reg reg);\nstd::unique_ptr<RelocatableInst> Msr(CPUMode cpuMode, Reg reg);\nstd::unique_ptr<RelocatableInst> Vmrs(CPUMode cpuMode, Reg reg);\nstd::unique_ptr<RelocatableInst> Vmsr(CPUMode cpuMode, Reg reg);\nstd::unique_ptr<RelocatableInst> Bkpt(CPUMode cpuMode, unsigned int value);\n\nstd::unique_ptr<RelocatableInst> T2it(CPUMode cpuMode, unsigned int cond,\n                                      unsigned pred);\n\nstd::vector<std::unique_ptr<RelocatableInst>>\nAddc(CPUMode cpuMode, RegLLVM dst, RegLLVM src, Constant val, RegLLVM temp);\nstd::vector<std::unique_ptr<RelocatableInst>> Addc(CPUMode cpuMode, RegLLVM dst,\n                                                   RegLLVM src, Constant val,\n                                                   RegLLVM temp,\n                                                   unsigned int cond);\n\nstd::unique_ptr<RelocatableInst> Cmp(CPUMode cpuMode, RegLLVM src, rword imm);\n\nstd::unique_ptr<RelocatableInst> Branch(CPUMode cpuMode, sword offset,\n                                        bool addBranchLen = false);\nstd::unique_ptr<RelocatableInst> BranchCC(CPUMode cpuMode, sword offset,\n                                          unsigned cond,\n                                          bool withinITBlock = false,\n                                          bool addBranchLen = false);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/MemoryAccess_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <memory>\n#include <stddef.h>\n#include <stdint.h>\n#include <utility>\n#include <vector>\n\n#include \"Target/ARM/ARMSubtarget.h\"\n#include \"Target/ARM/MCTargetDesc/ARMAddressingModes.h\"\n\n#include \"devVariable.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"Patch/ARM/PatchGenerator_ARM.h\"\n#include \"Patch/ARM/RelocatableInst_ARM.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/InstrRule.h\"\n#include \"Patch/MemoryAccess.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Callback.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n// PatchGenerator MemoryAccess Address/ReadValue/WriteValue Generator\n// ==================================================================\n\nnamespace {\n\ntypedef RelocatableInst::UniquePtrVec(AddressGenFn)(const Patch &patch,\n                                                    Reg dest, bool writeAccess);\n\n/* Address in a register\n * =====================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REG_FN(const Patch &patch, Reg dest,\n                                          unsigned operandOff) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff).getReg();\n  if (addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      return conv_unique<RelocatableInst>(\n          LoadImm::unique(dest, Constant(address + 4)));\n    } else {\n      return conv_unique<RelocatableInst>(\n          LoadImm::unique(dest, Constant(address + 8)));\n    }\n  } else {\n    return conv_unique<RelocatableInst>(MovReg::unique(dest, addrReg));\n  }\n}\n\n// address base in 1st operand\nconstexpr unsigned ADDR_REG_1_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDMIA,\n    llvm::ARM::LDMIA_UPD,\n    llvm::ARM::STMIA,\n    llvm::ARM::STMIA_UPD,\n    llvm::ARM::VLDMDIA,\n    llvm::ARM::VLDMDIA_UPD,\n    llvm::ARM::VLDMSIA,\n    llvm::ARM::VLDMSIA_UPD,\n    llvm::ARM::VST1LNd16,\n    llvm::ARM::VST1LNd32,\n    llvm::ARM::VST1LNd8,\n    llvm::ARM::VST1d16,\n    llvm::ARM::VST1d16Q,\n    llvm::ARM::VST1d16T,\n    llvm::ARM::VST1d32,\n    llvm::ARM::VST1d32Q,\n    llvm::ARM::VST1d32T,\n    llvm::ARM::VST1d64,\n    llvm::ARM::VST1d64Q,\n    llvm::ARM::VST1d64T,\n    llvm::ARM::VST1d8,\n    llvm::ARM::VST1d8Q,\n    llvm::ARM::VST1d8T,\n    llvm::ARM::VST1q16,\n    llvm::ARM::VST1q32,\n    llvm::ARM::VST1q64,\n    llvm::ARM::VST1q8,\n    llvm::ARM::VST2LNd16,\n    llvm::ARM::VST2LNd32,\n    llvm::ARM::VST2LNd8,\n    llvm::ARM::VST2LNq16,\n    llvm::ARM::VST2LNq32,\n    llvm::ARM::VST2b16,\n    llvm::ARM::VST2b32,\n    llvm::ARM::VST2b8,\n    llvm::ARM::VST2d16,\n    llvm::ARM::VST2d32,\n    llvm::ARM::VST2d8,\n    llvm::ARM::VST2q16,\n    llvm::ARM::VST2q32,\n    llvm::ARM::VST2q8,\n    llvm::ARM::VST3LNd16,\n    llvm::ARM::VST3LNd32,\n    llvm::ARM::VST3LNd8,\n    llvm::ARM::VST3LNq16,\n    llvm::ARM::VST3LNq32,\n    llvm::ARM::VST3d16,\n    llvm::ARM::VST3d32,\n    llvm::ARM::VST3d8,\n    llvm::ARM::VST3q16,\n    llvm::ARM::VST3q32,\n    llvm::ARM::VST3q8,\n    llvm::ARM::VST4LNd16,\n    llvm::ARM::VST4LNd32,\n    llvm::ARM::VST4LNd8,\n    llvm::ARM::VST4LNq16,\n    llvm::ARM::VST4LNq32,\n    llvm::ARM::VST4d16,\n    llvm::ARM::VST4d32,\n    llvm::ARM::VST4d8,\n    llvm::ARM::VST4q16,\n    llvm::ARM::VST4q32,\n    llvm::ARM::VST4q8,\n    llvm::ARM::VSTMDIA,\n    llvm::ARM::VSTMDIA_UPD,\n    llvm::ARM::VSTMSIA,\n    llvm::ARM::VSTMSIA_UPD,\n    llvm::ARM::t2LDMIA,\n    llvm::ARM::t2LDMIA_UPD,\n    llvm::ARM::t2STMIA,\n    llvm::ARM::t2STMIA_UPD,\n    llvm::ARM::tLDMIA,\n    llvm::ARM::tSTMIA_UPD,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_1_SIZE = sizeof(ADDR_REG_1_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_1_FN(const Patch &patch, Reg dest,\n                                            bool writeAccess) {\n  return ADDR_REG_FN(patch, dest, 0);\n}\n\n// address base in 2nd operand\nconstexpr unsigned ADDR_REG_2_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDA,\n    llvm::ARM::LDAB,\n    llvm::ARM::LDAEX,\n    llvm::ARM::LDAEXB,\n    llvm::ARM::LDAEXD,\n    llvm::ARM::LDAEXH,\n    llvm::ARM::LDAH,\n    llvm::ARM::LDREX,\n    llvm::ARM::LDREXB,\n    llvm::ARM::LDREXD,\n    llvm::ARM::LDREXH,\n    llvm::ARM::STL,\n    llvm::ARM::STLB,\n    llvm::ARM::STLH,\n    llvm::ARM::VLD1DUPd16,\n    llvm::ARM::VLD1DUPd32,\n    llvm::ARM::VLD1DUPd8,\n    llvm::ARM::VLD1DUPq16,\n    llvm::ARM::VLD1DUPq32,\n    llvm::ARM::VLD1DUPq8,\n    llvm::ARM::VLD1LNd16,\n    llvm::ARM::VLD1LNd16_UPD,\n    llvm::ARM::VLD1LNd32,\n    llvm::ARM::VLD1LNd32_UPD,\n    llvm::ARM::VLD1LNd8,\n    llvm::ARM::VLD1LNd8_UPD,\n    llvm::ARM::VLD1d16,\n    llvm::ARM::VLD1d16Q,\n    llvm::ARM::VLD1d16T,\n    llvm::ARM::VLD1d32,\n    llvm::ARM::VLD1d32Q,\n    llvm::ARM::VLD1d32T,\n    llvm::ARM::VLD1d64,\n    llvm::ARM::VLD1d64Q,\n    llvm::ARM::VLD1d64T,\n    llvm::ARM::VLD1d8,\n    llvm::ARM::VLD1d8Q,\n    llvm::ARM::VLD1d8T,\n    llvm::ARM::VLD1q16,\n    llvm::ARM::VLD1q32,\n    llvm::ARM::VLD1q64,\n    llvm::ARM::VLD1q8,\n    llvm::ARM::VLD2DUPd16,\n    llvm::ARM::VLD2DUPd16x2,\n    llvm::ARM::VLD2DUPd32,\n    llvm::ARM::VLD2DUPd32x2,\n    llvm::ARM::VLD2DUPd8,\n    llvm::ARM::VLD2DUPd8x2,\n    llvm::ARM::VLD2b16,\n    llvm::ARM::VLD2b32,\n    llvm::ARM::VLD2b8,\n    llvm::ARM::VLD2d16,\n    llvm::ARM::VLD2d32,\n    llvm::ARM::VLD2d8,\n    llvm::ARM::VLD2q16,\n    llvm::ARM::VLD2q32,\n    llvm::ARM::VLD2q8,\n    llvm::ARM::VLDR_FPCXTNS_post,\n    llvm::ARM::VLDR_FPCXTS_post,\n    llvm::ARM::VLDR_FPSCR_NZCVQC_post,\n    llvm::ARM::VLDR_FPSCR_post,\n    llvm::ARM::VLDR_P0_post,\n    llvm::ARM::VLDR_VPR_post,\n    llvm::ARM::VST1LNd16_UPD,\n    llvm::ARM::VST1LNd32_UPD,\n    llvm::ARM::VST1LNd8_UPD,\n    llvm::ARM::VST1d16Qwb_fixed,\n    llvm::ARM::VST1d16Qwb_register,\n    llvm::ARM::VST1d16Twb_fixed,\n    llvm::ARM::VST1d16Twb_register,\n    llvm::ARM::VST1d16wb_fixed,\n    llvm::ARM::VST1d16wb_register,\n    llvm::ARM::VST1d32Qwb_fixed,\n    llvm::ARM::VST1d32Qwb_register,\n    llvm::ARM::VST1d32Twb_fixed,\n    llvm::ARM::VST1d32Twb_register,\n    llvm::ARM::VST1d32wb_fixed,\n    llvm::ARM::VST1d32wb_register,\n    llvm::ARM::VST1d64Qwb_fixed,\n    llvm::ARM::VST1d64Qwb_register,\n    llvm::ARM::VST1d64Twb_fixed,\n    llvm::ARM::VST1d64Twb_register,\n    llvm::ARM::VST1d64wb_fixed,\n    llvm::ARM::VST1d64wb_register,\n    llvm::ARM::VST1d8Qwb_fixed,\n    llvm::ARM::VST1d8Qwb_register,\n    llvm::ARM::VST1d8Twb_fixed,\n    llvm::ARM::VST1d8Twb_register,\n    llvm::ARM::VST1d8wb_fixed,\n    llvm::ARM::VST1d8wb_register,\n    llvm::ARM::VST1q16wb_fixed,\n    llvm::ARM::VST1q16wb_register,\n    llvm::ARM::VST1q32wb_fixed,\n    llvm::ARM::VST1q32wb_register,\n    llvm::ARM::VST1q64wb_fixed,\n    llvm::ARM::VST1q64wb_register,\n    llvm::ARM::VST1q8wb_fixed,\n    llvm::ARM::VST1q8wb_register,\n    llvm::ARM::VST2LNd16_UPD,\n    llvm::ARM::VST2LNd32_UPD,\n    llvm::ARM::VST2LNd8_UPD,\n    llvm::ARM::VST2LNq16_UPD,\n    llvm::ARM::VST2LNq32_UPD,\n    llvm::ARM::VST2b16wb_fixed,\n    llvm::ARM::VST2b16wb_register,\n    llvm::ARM::VST2b32wb_fixed,\n    llvm::ARM::VST2b32wb_register,\n    llvm::ARM::VST2b8wb_fixed,\n    llvm::ARM::VST2b8wb_register,\n    llvm::ARM::VST2d16wb_fixed,\n    llvm::ARM::VST2d16wb_register,\n    llvm::ARM::VST2d32wb_fixed,\n    llvm::ARM::VST2d32wb_register,\n    llvm::ARM::VST2d8wb_fixed,\n    llvm::ARM::VST2d8wb_register,\n    llvm::ARM::VST2q16wb_fixed,\n    llvm::ARM::VST2q16wb_register,\n    llvm::ARM::VST2q32wb_fixed,\n    llvm::ARM::VST2q32wb_register,\n    llvm::ARM::VST2q8wb_fixed,\n    llvm::ARM::VST2q8wb_register,\n    llvm::ARM::VST3LNd16_UPD,\n    llvm::ARM::VST3LNd32_UPD,\n    llvm::ARM::VST3LNd8_UPD,\n    llvm::ARM::VST3LNq16_UPD,\n    llvm::ARM::VST3LNq32_UPD,\n    llvm::ARM::VST3d16_UPD,\n    llvm::ARM::VST3d32_UPD,\n    llvm::ARM::VST3d8_UPD,\n    llvm::ARM::VST3q16_UPD,\n    llvm::ARM::VST3q32_UPD,\n    llvm::ARM::VST3q8_UPD,\n    llvm::ARM::VST4LNd16_UPD,\n    llvm::ARM::VST4LNd32_UPD,\n    llvm::ARM::VST4LNd8_UPD,\n    llvm::ARM::VST4LNq16_UPD,\n    llvm::ARM::VST4LNq32_UPD,\n    llvm::ARM::VST4d16_UPD,\n    llvm::ARM::VST4d32_UPD,\n    llvm::ARM::VST4d8_UPD,\n    llvm::ARM::VST4q16_UPD,\n    llvm::ARM::VST4q32_UPD,\n    llvm::ARM::VST4q8_UPD,\n    llvm::ARM::VSTR_FPCXTNS_pre,\n    llvm::ARM::VSTR_FPCXTS_pre,\n    llvm::ARM::VSTR_FPSCR_NZCVQC_pre,\n    llvm::ARM::VSTR_FPSCR_pre,\n    llvm::ARM::VSTR_P0_pre,\n    llvm::ARM::VSTR_VPR_pre,\n    llvm::ARM::t2LDA,\n    llvm::ARM::t2LDAB,\n    llvm::ARM::t2LDAEX,\n    llvm::ARM::t2LDAEXB,\n    llvm::ARM::t2LDAEXH,\n    llvm::ARM::t2LDAH,\n    llvm::ARM::t2LDREXB,\n    llvm::ARM::t2LDREXH,\n    llvm::ARM::t2STL,\n    llvm::ARM::t2STLB,\n    llvm::ARM::t2STLH,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_2_SIZE = sizeof(ADDR_REG_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_2_FN(const Patch &patch, Reg dest,\n                                            bool writeAccess) {\n  return ADDR_REG_FN(patch, dest, 1);\n}\n\n// address base in 3rd operand\nconstexpr unsigned ADDR_REG_3_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRBT_POST_IMM,\n    llvm::ARM::LDRBT_POST_REG,\n    llvm::ARM::LDRB_POST_IMM,\n    llvm::ARM::LDRB_POST_REG,\n    llvm::ARM::LDRHTi,\n    llvm::ARM::LDRHTr,\n    llvm::ARM::LDRH_POST,\n    llvm::ARM::LDRSBTi,\n    llvm::ARM::LDRSBTr,\n    llvm::ARM::LDRSB_POST,\n    llvm::ARM::LDRSHTi,\n    llvm::ARM::LDRSHTr,\n    llvm::ARM::LDRSH_POST,\n    llvm::ARM::LDRT_POST_IMM,\n    llvm::ARM::LDRT_POST_REG,\n    llvm::ARM::LDR_POST_IMM,\n    llvm::ARM::LDR_POST_REG,\n    llvm::ARM::STLEX,\n    llvm::ARM::STLEXB,\n    llvm::ARM::STLEXD,\n    llvm::ARM::STLEXH,\n    llvm::ARM::STRBT_POST_IMM,\n    llvm::ARM::STRBT_POST_REG,\n    llvm::ARM::STRB_POST_IMM,\n    llvm::ARM::STRB_POST_REG,\n    llvm::ARM::STREX,\n    llvm::ARM::STREXB,\n    llvm::ARM::STREXD,\n    llvm::ARM::STREXH,\n    llvm::ARM::STRH_POST,\n    llvm::ARM::STRT_POST_IMM,\n    llvm::ARM::STRT_POST_REG,\n    llvm::ARM::STR_POST_IMM,\n    llvm::ARM::STR_POST_REG,\n    llvm::ARM::SWP,\n    llvm::ARM::SWPB,\n    llvm::ARM::VLD1DUPd16wb_fixed,\n    llvm::ARM::VLD1DUPd16wb_register,\n    llvm::ARM::VLD1DUPd32wb_fixed,\n    llvm::ARM::VLD1DUPd32wb_register,\n    llvm::ARM::VLD1DUPd8wb_fixed,\n    llvm::ARM::VLD1DUPd8wb_register,\n    llvm::ARM::VLD1DUPq16wb_fixed,\n    llvm::ARM::VLD1DUPq16wb_register,\n    llvm::ARM::VLD1DUPq32wb_fixed,\n    llvm::ARM::VLD1DUPq32wb_register,\n    llvm::ARM::VLD1DUPq8wb_fixed,\n    llvm::ARM::VLD1DUPq8wb_register,\n    llvm::ARM::VLD1d16Qwb_fixed,\n    llvm::ARM::VLD1d16Qwb_register,\n    llvm::ARM::VLD1d16Twb_fixed,\n    llvm::ARM::VLD1d16Twb_register,\n    llvm::ARM::VLD1d16wb_fixed,\n    llvm::ARM::VLD1d16wb_register,\n    llvm::ARM::VLD1d32Qwb_fixed,\n    llvm::ARM::VLD1d32Qwb_register,\n    llvm::ARM::VLD1d32Twb_fixed,\n    llvm::ARM::VLD1d32Twb_register,\n    llvm::ARM::VLD1d32wb_fixed,\n    llvm::ARM::VLD1d32wb_register,\n    llvm::ARM::VLD1d64Qwb_fixed,\n    llvm::ARM::VLD1d64Qwb_register,\n    llvm::ARM::VLD1d64Twb_fixed,\n    llvm::ARM::VLD1d64Twb_register,\n    llvm::ARM::VLD1d64wb_fixed,\n    llvm::ARM::VLD1d64wb_register,\n    llvm::ARM::VLD1d8Qwb_fixed,\n    llvm::ARM::VLD1d8Qwb_register,\n    llvm::ARM::VLD1d8Twb_fixed,\n    llvm::ARM::VLD1d8Twb_register,\n    llvm::ARM::VLD1d8wb_fixed,\n    llvm::ARM::VLD1d8wb_register,\n    llvm::ARM::VLD1q16wb_fixed,\n    llvm::ARM::VLD1q16wb_register,\n    llvm::ARM::VLD1q32wb_fixed,\n    llvm::ARM::VLD1q32wb_register,\n    llvm::ARM::VLD1q64wb_fixed,\n    llvm::ARM::VLD1q64wb_register,\n    llvm::ARM::VLD1q8wb_fixed,\n    llvm::ARM::VLD1q8wb_register,\n    llvm::ARM::VLD2DUPd16wb_fixed,\n    llvm::ARM::VLD2DUPd16wb_register,\n    llvm::ARM::VLD2DUPd16x2wb_fixed,\n    llvm::ARM::VLD2DUPd16x2wb_register,\n    llvm::ARM::VLD2DUPd32wb_fixed,\n    llvm::ARM::VLD2DUPd32wb_register,\n    llvm::ARM::VLD2DUPd32x2wb_fixed,\n    llvm::ARM::VLD2DUPd32x2wb_register,\n    llvm::ARM::VLD2DUPd8wb_fixed,\n    llvm::ARM::VLD2DUPd8wb_register,\n    llvm::ARM::VLD2DUPd8x2wb_fixed,\n    llvm::ARM::VLD2DUPd8x2wb_register,\n    llvm::ARM::VLD2LNd16,\n    llvm::ARM::VLD2LNd32,\n    llvm::ARM::VLD2LNd8,\n    llvm::ARM::VLD2LNq16,\n    llvm::ARM::VLD2LNq32,\n    llvm::ARM::VLD2b16wb_fixed,\n    llvm::ARM::VLD2b16wb_register,\n    llvm::ARM::VLD2b32wb_fixed,\n    llvm::ARM::VLD2b32wb_register,\n    llvm::ARM::VLD2b8wb_fixed,\n    llvm::ARM::VLD2b8wb_register,\n    llvm::ARM::VLD2d16wb_fixed,\n    llvm::ARM::VLD2d16wb_register,\n    llvm::ARM::VLD2d32wb_fixed,\n    llvm::ARM::VLD2d32wb_register,\n    llvm::ARM::VLD2d8wb_fixed,\n    llvm::ARM::VLD2d8wb_register,\n    llvm::ARM::VLD2q16wb_fixed,\n    llvm::ARM::VLD2q16wb_register,\n    llvm::ARM::VLD2q32wb_fixed,\n    llvm::ARM::VLD2q32wb_register,\n    llvm::ARM::VLD2q8wb_fixed,\n    llvm::ARM::VLD2q8wb_register,\n    llvm::ARM::t2LDAEXD,\n    llvm::ARM::t2LDRB_POST,\n    llvm::ARM::t2LDREXD,\n    llvm::ARM::t2LDRH_POST,\n    llvm::ARM::t2LDRSB_POST,\n    llvm::ARM::t2LDRSH_POST,\n    llvm::ARM::t2LDR_POST,\n    llvm::ARM::t2STLEX,\n    llvm::ARM::t2STLEXB,\n    llvm::ARM::t2STLEXH,\n    llvm::ARM::t2STRB_POST,\n    llvm::ARM::t2STREXB,\n    llvm::ARM::t2STREXH,\n    llvm::ARM::t2STRH_POST,\n    llvm::ARM::t2STR_POST,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_3_SIZE = sizeof(ADDR_REG_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_3_FN(const Patch &patch, Reg dest,\n                                            bool writeAccess) {\n  return ADDR_REG_FN(patch, dest, 2);\n}\n\n// address base in 4th operand\nconstexpr unsigned ADDR_REG_4_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRD_POST,\n    llvm::ARM::STRD_POST,\n    llvm::ARM::VLD2LNd16_UPD,\n    llvm::ARM::VLD2LNd32_UPD,\n    llvm::ARM::VLD2LNd8_UPD,\n    llvm::ARM::VLD2LNq16_UPD,\n    llvm::ARM::VLD2LNq32_UPD,\n    llvm::ARM::VLD3DUPd16,\n    llvm::ARM::VLD3DUPd32,\n    llvm::ARM::VLD3DUPd8,\n    llvm::ARM::VLD3DUPq16,\n    llvm::ARM::VLD3DUPq32,\n    llvm::ARM::VLD3DUPq8,\n    llvm::ARM::VLD3LNd16,\n    llvm::ARM::VLD3LNd32,\n    llvm::ARM::VLD3LNd8,\n    llvm::ARM::VLD3LNq16,\n    llvm::ARM::VLD3LNq32,\n    llvm::ARM::VLD3d16,\n    llvm::ARM::VLD3d32,\n    llvm::ARM::VLD3d8,\n    llvm::ARM::VLD3q16,\n    llvm::ARM::VLD3q32,\n    llvm::ARM::VLD3q8,\n    llvm::ARM::t2LDRD_POST,\n    llvm::ARM::t2STLEXD,\n    llvm::ARM::t2STRD_POST,\n    llvm::ARM::t2STREXD,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_4_SIZE = sizeof(ADDR_REG_4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_4_FN(const Patch &patch, Reg dest,\n                                            bool writeAccess) {\n  return ADDR_REG_FN(patch, dest, 3);\n}\n\n// address base in 5th operand\nconstexpr unsigned ADDR_REG_5_TABLE[] = {\n    // clang-format off\n    llvm::ARM::VLD3DUPd16_UPD,\n    llvm::ARM::VLD3DUPd32_UPD,\n    llvm::ARM::VLD3DUPd8_UPD,\n    llvm::ARM::VLD3DUPq16_UPD,\n    llvm::ARM::VLD3DUPq32_UPD,\n    llvm::ARM::VLD3DUPq8_UPD,\n    llvm::ARM::VLD3LNd16_UPD,\n    llvm::ARM::VLD3LNd32_UPD,\n    llvm::ARM::VLD3LNd8_UPD,\n    llvm::ARM::VLD3LNq16_UPD,\n    llvm::ARM::VLD3LNq32_UPD,\n    llvm::ARM::VLD3d16_UPD,\n    llvm::ARM::VLD3d32_UPD,\n    llvm::ARM::VLD3d8_UPD,\n    llvm::ARM::VLD3q16_UPD,\n    llvm::ARM::VLD3q32_UPD,\n    llvm::ARM::VLD3q8_UPD,\n    llvm::ARM::VLD4DUPd16,\n    llvm::ARM::VLD4DUPd32,\n    llvm::ARM::VLD4DUPd8,\n    llvm::ARM::VLD4DUPq16,\n    llvm::ARM::VLD4DUPq32,\n    llvm::ARM::VLD4DUPq8,\n    llvm::ARM::VLD4LNd16,\n    llvm::ARM::VLD4LNd32,\n    llvm::ARM::VLD4LNd8,\n    llvm::ARM::VLD4LNq16,\n    llvm::ARM::VLD4LNq32,\n    llvm::ARM::VLD4d16,\n    llvm::ARM::VLD4d32,\n    llvm::ARM::VLD4d8,\n    llvm::ARM::VLD4q16,\n    llvm::ARM::VLD4q32,\n    llvm::ARM::VLD4q8,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_5_SIZE = sizeof(ADDR_REG_5_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_5_FN(const Patch &patch, Reg dest,\n                                            bool writeAccess) {\n  return ADDR_REG_FN(patch, dest, 4);\n}\n\n// address base in 6th operand\nconstexpr unsigned ADDR_REG_6_TABLE[] = {\n    // clang-format off\n    llvm::ARM::VLD4DUPd16_UPD,\n    llvm::ARM::VLD4DUPd32_UPD,\n    llvm::ARM::VLD4DUPd8_UPD,\n    llvm::ARM::VLD4DUPq16_UPD,\n    llvm::ARM::VLD4DUPq32_UPD,\n    llvm::ARM::VLD4DUPq8_UPD,\n    llvm::ARM::VLD4LNd16_UPD,\n    llvm::ARM::VLD4LNd32_UPD,\n    llvm::ARM::VLD4LNd8_UPD,\n    llvm::ARM::VLD4LNq16_UPD,\n    llvm::ARM::VLD4LNq32_UPD,\n    llvm::ARM::VLD4d16_UPD,\n    llvm::ARM::VLD4d32_UPD,\n    llvm::ARM::VLD4d8_UPD,\n    llvm::ARM::VLD4q16_UPD,\n    llvm::ARM::VLD4q32_UPD,\n    llvm::ARM::VLD4q8_UPD,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_6_SIZE = sizeof(ADDR_REG_6_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_6_FN(const Patch &patch, Reg dest,\n                                            bool writeAccess) {\n  return ADDR_REG_FN(patch, dest, 5);\n}\n\n/* Address in a register with fixed offset\n * =======================================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REG_PLUS_FN(const Patch &patch, Reg dest,\n                                               unsigned operandOff,\n                                               int offset) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(operandOff < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff).getReg();\n  QBDI_REQUIRE_ABORT(addrReg != llvm::ARM::PC, \"Unexpected PC register {}\",\n                     patch);\n  return Addc(llvmcpu, dest, addrReg, offset, dest);\n}\n\n// address base in 1st operand + 4 (no PC)\nconstexpr unsigned ADDR_REG_1_PLUS4_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDMIB,\n    llvm::ARM::LDMIB_UPD,\n    llvm::ARM::STMIB,\n    llvm::ARM::STMIB_UPD,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_1_PLUS4_SIZE =\n    sizeof(ADDR_REG_1_PLUS4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_1_PLUS4_FN(const Patch &patch, Reg dest,\n                                                  bool writeAccess) {\n  return ADDR_REG_PLUS_FN(patch, dest, 0, sizeof(rword));\n}\n\n// address base in 1st operand - dynamique argument size (no PC)\nconstexpr unsigned ADDR_REG_1_DYN_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDMDB,\n    llvm::ARM::LDMDB_UPD,\n    llvm::ARM::STMDB,\n    llvm::ARM::STMDB_UPD,\n    llvm::ARM::VLDMDDB_UPD,\n    llvm::ARM::VLDMSDB_UPD,\n    llvm::ARM::VSTMDDB_UPD,\n    llvm::ARM::VSTMSDB_UPD,\n    llvm::ARM::t2LDMDB,\n    llvm::ARM::t2LDMDB_UPD,\n    llvm::ARM::t2STMDB,\n    llvm::ARM::t2STMDB_UPD,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_1_DYN_SIZE =\n    sizeof(ADDR_REG_1_DYN_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_1_DYN_FN(const Patch &patch, Reg dest,\n                                                bool writeAccess) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  if (writeAccess) {\n    return ADDR_REG_PLUS_FN(patch, dest, 0, -getWriteSize(inst, llvmcpu));\n  } else {\n    return ADDR_REG_PLUS_FN(patch, dest, 0, -getReadSize(inst, llvmcpu));\n  }\n}\n\n// address base in 1st operand - dynamique argument size + 4 (no PC)\nconstexpr unsigned ADDR_REG_1_DYN_PLUS4_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDMDA,\n    llvm::ARM::LDMDA_UPD,\n    llvm::ARM::STMDA,\n    llvm::ARM::STMDA_UPD,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_1_DYN_PLUS4_SIZE =\n    sizeof(ADDR_REG_1_DYN_PLUS4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_1_DYN_PLUS4_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  if (writeAccess) {\n    return ADDR_REG_PLUS_FN(patch, dest, 0,\n                            sizeof(rword) - getWriteSize(inst, llvmcpu));\n  } else {\n    return ADDR_REG_PLUS_FN(patch, dest, 0,\n                            sizeof(rword) - getReadSize(inst, llvmcpu));\n  }\n}\n\n/* Address in a register + immediate\n * =================================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REG_SIMM_FN(const Patch &patch, Reg dest,\n                                               unsigned operandOff1,\n                                               unsigned operandOff2) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  sword offset = inst.getOperand(operandOff2).getImm();\n  // encoding of #-0x0\n  if (offset == INT32_MIN) {\n    offset = 0;\n  }\n  if (addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      return conv_unique<RelocatableInst>(\n          LoadImm::unique(dest, Constant(address + 4 + offset)));\n    } else {\n      return conv_unique<RelocatableInst>(\n          LoadImm::unique(dest, Constant(address + 8 + offset)));\n    }\n  } else {\n    return Addc(llvmcpu, dest, addrReg, offset, dest);\n  }\n}\n\n// address base in 1st operand + signed imm13 offset in the 2nd\nconstexpr unsigned ADDR_REG_1_SIMM_2_TABLE[] = {\n    // clang-format off\n    llvm::ARM::VLDR_FPCXTNS_off,\n    llvm::ARM::VLDR_FPCXTS_off,\n    llvm::ARM::VLDR_FPSCR_NZCVQC_off,\n    llvm::ARM::VLDR_FPSCR_off,\n    llvm::ARM::VLDR_P0_off,\n    llvm::ARM::VLDR_VPR_off,\n    llvm::ARM::VSTR_FPCXTNS_off,\n    llvm::ARM::VSTR_FPCXTS_off,\n    llvm::ARM::VSTR_FPSCR_NZCVQC_off,\n    llvm::ARM::VSTR_FPSCR_off,\n    llvm::ARM::VSTR_P0_off,\n    llvm::ARM::VSTR_VPR_off,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_1_SIMM_2_SIZE =\n    sizeof(ADDR_REG_1_SIMM_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_1_SIMM_2_FN(const Patch &patch, Reg dest,\n                                                   bool writeAccess) {\n  return ADDR_REG_SIMM_FN(patch, dest, 0, 1);\n}\n\n// address base in 2nd operand + signed imm13 offset in the 3rd\nconstexpr unsigned ADDR_REG_2_SIMM_3_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRBi12,\n    llvm::ARM::LDRi12,\n    llvm::ARM::STRBi12,\n    llvm::ARM::STRi12,\n    llvm::ARM::VLDR_FPCXTNS_pre,\n    llvm::ARM::VLDR_FPCXTS_pre,\n    llvm::ARM::VLDR_FPSCR_NZCVQC_pre,\n    llvm::ARM::VLDR_FPSCR_pre,\n    llvm::ARM::VLDR_P0_pre,\n    llvm::ARM::VLDR_VPR_pre,\n    llvm::ARM::VSTR_FPCXTNS_post,\n    llvm::ARM::VSTR_FPCXTS_post,\n    llvm::ARM::VSTR_FPSCR_NZCVQC_post,\n    llvm::ARM::VSTR_FPSCR_post,\n    llvm::ARM::VSTR_P0_post,\n    llvm::ARM::VSTR_VPR_post,\n    // unsigned imm8\n    llvm::ARM::t2LDRBT,\n    llvm::ARM::t2LDRT,\n    llvm::ARM::t2STRBT,\n    llvm::ARM::t2STRT,\n    // unsigned imm12\n    llvm::ARM::t2LDRBi12,\n    llvm::ARM::t2LDRHi12,\n    llvm::ARM::t2LDRSBi12,\n    llvm::ARM::t2LDRSHi12,\n    llvm::ARM::t2LDRi12,\n    llvm::ARM::t2STRBi12,\n    llvm::ARM::t2STRHi12,\n    llvm::ARM::t2STRi12,\n    // signed imm8\n    llvm::ARM::t2LDRBi8,\n    llvm::ARM::t2LDRHi8,\n    llvm::ARM::t2LDRSBi8,\n    llvm::ARM::t2LDRSHi8,\n    llvm::ARM::t2LDRi8,\n    llvm::ARM::t2STRBi8,\n    llvm::ARM::t2STRHi8,\n    llvm::ARM::t2STRi8,\n    // unsigned imm5\n    llvm::ARM::tLDRBi,\n    llvm::ARM::tSTRBi,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_2_SIMM_3_SIZE =\n    sizeof(ADDR_REG_2_SIMM_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_2_SIMM_3_FN(const Patch &patch, Reg dest,\n                                                   bool writeAccess) {\n  return ADDR_REG_SIMM_FN(patch, dest, 1, 2);\n}\n\n// address base in 3rd operand + signed imm13 offset in the 4th\nconstexpr unsigned ADDR_REG_3_SIMM_4_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRB_PRE_IMM,\n    llvm::ARM::LDR_PRE_IMM,\n    llvm::ARM::STRB_PRE_IMM,\n    llvm::ARM::STR_PRE_IMM,\n    // signed imm8\n    llvm::ARM::t2LDRB_PRE,\n    llvm::ARM::t2LDRDi8,\n    llvm::ARM::t2LDRH_PRE,\n    llvm::ARM::t2LDRSB_PRE,\n    llvm::ARM::t2LDRSH_PRE,\n    llvm::ARM::t2LDR_PRE,\n    llvm::ARM::t2STRB_PRE,\n    llvm::ARM::t2STRDi8,\n    llvm::ARM::t2STRH_PRE,\n    llvm::ARM::t2STR_PRE,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_3_SIMM_4_SIZE =\n    sizeof(ADDR_REG_3_SIMM_4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_3_SIMM_4_FN(const Patch &patch, Reg dest,\n                                                   bool writeAccess) {\n  return ADDR_REG_SIMM_FN(patch, dest, 2, 3);\n}\n\n// address base in 4th operand + signed imm8 offset in the 5th\nconstexpr unsigned ADDR_REG_4_SIMM_5_TABLE[] = {\n    // clang-format off\n    // signed imm8\n    llvm::ARM::t2LDRD_PRE,\n    llvm::ARM::t2STRD_PRE,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_4_SIMM_5_SIZE =\n    sizeof(ADDR_REG_4_SIMM_5_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_4_SIMM_5_FN(const Patch &patch, Reg dest,\n                                                   bool writeAccess) {\n  return ADDR_REG_SIMM_FN(patch, dest, 3, 4);\n}\n\n/* Address in a register + register\n * ================================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REG_REG_FN(const Patch &patch, Reg dest,\n                                              unsigned operandOff1,\n                                              unsigned operandOff2) {\n\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  RegLLVM offsetReg = inst.getOperand(operandOff2).getReg();\n  QBDI_REQUIRE_ABORT(offsetReg != llvm::ARM::NoRegister,\n                     \"Missing offset register {}\", patch);\n\n  RelocatableInst::UniquePtrVec reloc;\n\n  if (offsetReg == llvm::ARM::PC or addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      reloc.push_back(LoadImm::unique(dest, address + 4));\n    } else {\n      reloc.push_back(LoadImm::unique(dest, address + 8));\n    }\n    if (offsetReg == llvm::ARM::PC) {\n      offsetReg = dest;\n    }\n    if (addrReg == llvm::ARM::PC) {\n      addrReg = dest;\n    }\n  }\n\n  reloc.push_back(Addr(llvmcpu, dest, addrReg, offsetReg));\n  return reloc;\n}\n\n// address base in 1st operand + register in the 2nd\nconstexpr unsigned ADDR_REG_1_REG_2_TABLE[] = {\n    // clang-format off\n    llvm::ARM::t2TBB,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_1_REG_2_SIZE =\n    sizeof(ADDR_REG_1_REG_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_1_REG_2_FN(const Patch &patch, Reg dest,\n                                                  bool writeAccess) {\n  return ADDR_REG_REG_FN(patch, dest, 0, 1);\n}\n\n// address base in 2nd operand + register in the 3rd\nconstexpr unsigned ADDR_REG_2_REG_3_TABLE[] = {\n    // clang-format off\n    llvm::ARM::tLDRBr,\n    llvm::ARM::tLDRHr,\n    llvm::ARM::tLDRSB,\n    llvm::ARM::tLDRSH,\n    llvm::ARM::tLDRr,\n    llvm::ARM::tSTRBr,\n    llvm::ARM::tSTRHr,\n    llvm::ARM::tSTRr,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_2_REG_3_SIZE =\n    sizeof(ADDR_REG_2_REG_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_2_REG_3_FN(const Patch &patch, Reg dest,\n                                                  bool writeAccess) {\n  return ADDR_REG_REG_FN(patch, dest, 1, 2);\n}\n\n/* Address in a register + register LSL #1\n * =======================================\n */\n\nconstexpr unsigned ADDR_REG_1_REGSHIFT1_2_TABLE[] = {\n    // clang-format off\n    llvm::ARM::t2TBH,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_1_REGSHIFT1_2_SIZE =\n    sizeof(ADDR_REG_1_REGSHIFT1_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_1_REGSHIFT1_2_FN(const Patch &patch, Reg dest, bool writeAccess) {\n\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(2 <= inst.getNumOperands(), \"Invalid operand number {}\",\n                     patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  RegLLVM addrReg = inst.getOperand(0).getReg();\n  RegLLVM offsetReg = inst.getOperand(1).getReg();\n  QBDI_REQUIRE_ABORT(offsetReg != llvm::ARM::NoRegister,\n                     \"Missing offset register {}\", patch);\n\n  RelocatableInst::UniquePtrVec reloc;\n\n  if (offsetReg == llvm::ARM::PC or addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      reloc.push_back(LoadImm::unique(dest, address + 4));\n    } else {\n      reloc.push_back(LoadImm::unique(dest, address + 8));\n    }\n    if (offsetReg == llvm::ARM::PC) {\n      offsetReg = dest;\n    }\n    if (addrReg == llvm::ARM::PC) {\n      addrReg = dest;\n    }\n  }\n\n  reloc.push_back(\n      Addrs(llvmcpu, dest, addrReg, offsetReg, 1, llvm::ARM_AM::lsl));\n  return reloc;\n}\n\n/* Address in a register + LSL register\n * ====================================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REG_REGLSL_FN(const Patch &patch, Reg dest,\n                                                 unsigned operandOff1,\n                                                 unsigned operandOff2,\n                                                 unsigned operandOff3) {\n\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff3 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff3).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  RegLLVM offsetReg = inst.getOperand(operandOff2).getReg();\n  unsigned shift = inst.getOperand(operandOff3).getImm();\n  QBDI_REQUIRE_ABORT(offsetReg != llvm::ARM::NoRegister,\n                     \"Missing offset register {}\", patch);\n\n  RelocatableInst::UniquePtrVec reloc;\n\n  if (offsetReg == llvm::ARM::PC or addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      reloc.push_back(LoadImm::unique(dest, address + 4));\n    } else {\n      reloc.push_back(LoadImm::unique(dest, address + 8));\n    }\n    if (offsetReg == llvm::ARM::PC) {\n      offsetReg = dest;\n    }\n    if (addrReg == llvm::ARM::PC) {\n      addrReg = dest;\n    }\n  }\n\n  if (shift == 0) {\n    reloc.push_back(Addr(llvmcpu, dest, addrReg, offsetReg));\n  } else {\n    reloc.push_back(\n        Addrs(llvmcpu, dest, addrReg, offsetReg, shift, llvm::ARM_AM::lsl));\n  }\n  return reloc;\n}\n\n// address base in 2nd operand + shifted register in the 3rd + shift imm in 4th\nconstexpr unsigned ADDR_REG_2_REGLSL_3_TABLE[] = {\n    // clang-format off\n    llvm::ARM::t2LDRBs,\n    llvm::ARM::t2LDRHs,\n    llvm::ARM::t2LDRSBs,\n    llvm::ARM::t2LDRSHs,\n    llvm::ARM::t2LDRs,\n    llvm::ARM::t2STRBs,\n    llvm::ARM::t2STRHs,\n    llvm::ARM::t2STRs,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_2_REGLSL_3_SIZE =\n    sizeof(ADDR_REG_2_REGLSL_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_2_REGLSL_3_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_REGLSL_FN(patch, dest, 1, 2, 3);\n}\n\n/* Address in a register + shifted register\n * ========================================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REG_REGSHIFT_FN(const Patch &patch, Reg dest,\n                                                   unsigned operandOff1,\n                                                   unsigned operandOff2,\n                                                   unsigned operandOff3) {\n\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff3 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff3).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  RegLLVM offsetReg = inst.getOperand(operandOff2).getReg();\n  unsigned value = inst.getOperand(operandOff3).getImm();\n  QBDI_REQUIRE_ABORT(offsetReg != llvm::ARM::NoRegister,\n                     \"Missing offset register {}\", patch);\n\n  unsigned shift = llvm::ARM_AM::getAM2Offset(value);\n  bool offsetIsSub = llvm::ARM_AM::getAM2Op(value) == llvm::ARM_AM::sub;\n  llvm::ARM_AM::ShiftOpc shiftType = llvm::ARM_AM::getAM2ShiftOpc(value);\n\n  RelocatableInst::UniquePtrVec reloc;\n\n  if (offsetReg == llvm::ARM::PC or addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      reloc.push_back(LoadImm::unique(dest, address + 4));\n    } else {\n      reloc.push_back(LoadImm::unique(dest, address + 8));\n    }\n    if (offsetReg == llvm::ARM::PC) {\n      offsetReg = dest;\n    }\n    if (addrReg == llvm::ARM::PC) {\n      addrReg = dest;\n    }\n  }\n\n  if (shift == 0 and shiftType == llvm::ARM_AM::no_shift) {\n    if (offsetIsSub) {\n      reloc.push_back(Subr(llvmcpu, dest, addrReg, offsetReg));\n    } else {\n      reloc.push_back(Addr(llvmcpu, dest, addrReg, offsetReg));\n    }\n  } else {\n    if (offsetIsSub) {\n      reloc.push_back(\n          Subrs(llvmcpu, dest, addrReg, offsetReg, shift, shiftType));\n    } else {\n      reloc.push_back(\n          Addrs(llvmcpu, dest, addrReg, offsetReg, shift, shiftType));\n    }\n  }\n  return reloc;\n}\n\n// address base in 2nd operand + shifted register in the 3rd + shift imm in 4th\nconstexpr unsigned ADDR_REG_2_REGSHIFT_3_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRBrs,\n    llvm::ARM::LDRrs,\n    llvm::ARM::STRBrs,\n    llvm::ARM::STRrs,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_2_REGSHIFT_3_SIZE =\n    sizeof(ADDR_REG_2_REGSHIFT_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_2_REGSHIFT_3_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_REGSHIFT_FN(patch, dest, 1, 2, 3);\n}\n\n// address base in 3rd operand + shifted register in the 4th + shift imm in 5th\nconstexpr unsigned ADDR_REG_3_REGSHIFT_4_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRB_PRE_REG,\n    llvm::ARM::LDR_PRE_REG,\n    llvm::ARM::STRB_PRE_REG,\n    llvm::ARM::STR_PRE_REG,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_3_REGSHIFT_4_SIZE =\n    sizeof(ADDR_REG_3_REGSHIFT_4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_3_REGSHIFT_4_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_REGSHIFT_FN(patch, dest, 2, 3, 4);\n}\n\n/* Address in a register with (+/- register) or (+/- imm8)\n * =======================================================\n */\nRelocatableInst::UniquePtrVec ADDR_REG_IMMORREG_FN(const Patch &patch, Reg dest,\n                                                   unsigned operandOff1,\n                                                   unsigned operandOff2,\n                                                   unsigned operandOff3) {\n\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff3 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff3).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  RegLLVM offsetReg = inst.getOperand(operandOff2).getReg();\n  sword imm = inst.getOperand(operandOff3).getImm();\n\n  bool offsetIsSub = (((imm >> 8) & 1) == 1);\n  imm = (imm & 0xff);\n\n  RelocatableInst::UniquePtrVec reloc;\n\n  if (offsetReg == llvm::ARM::PC or addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      reloc.push_back(LoadImm::unique(dest, address + 4));\n    } else {\n      reloc.push_back(LoadImm::unique(dest, address + 8));\n    }\n    if (offsetReg == llvm::ARM::PC) {\n      offsetReg = dest;\n    }\n    if (addrReg == llvm::ARM::PC) {\n      addrReg = dest;\n    }\n  }\n\n  if (offsetReg == llvm::ARM::NoRegister) {\n    if (offsetIsSub) {\n      reloc.push_back(NoReloc::unique(sub(dest, addrReg, imm)));\n    } else {\n      reloc.push_back(NoReloc::unique(add(dest, addrReg, imm)));\n    }\n  } else {\n    if (offsetIsSub) {\n      reloc.push_back(NoReloc::unique(subr(dest, addrReg, offsetReg)));\n    } else {\n      reloc.push_back(NoReloc::unique(addr(dest, addrReg, offsetReg)));\n    }\n  }\n  return reloc;\n}\n\n// address base in 2nd operand + register in the 3rd + imm in 4th\nconstexpr unsigned ADDR_REG_IMMORREG_2_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRH,\n    llvm::ARM::LDRSB,\n    llvm::ARM::LDRSH,\n    llvm::ARM::STRH,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_IMMORREG_2_SIZE =\n    sizeof(ADDR_REG_IMMORREG_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_IMMORREG_2_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_IMMORREG_FN(patch, dest, 1, 2, 3);\n}\n\n// address base in 3rd operand + register in the 4th + imm in 5th\nconstexpr unsigned ADDR_REG_IMMORREG_3_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRD,\n    llvm::ARM::LDRH_PRE,\n    llvm::ARM::LDRSB_PRE,\n    llvm::ARM::LDRSH_PRE,\n    llvm::ARM::STRD,\n    llvm::ARM::STRH_PRE,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_IMMORREG_3_SIZE =\n    sizeof(ADDR_REG_IMMORREG_3_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_IMMORREG_3_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_IMMORREG_FN(patch, dest, 2, 3, 4);\n}\n\n// address base in 4th operand + register in the 5th + imm in 6th\nconstexpr unsigned ADDR_REG_IMMORREG_4_TABLE[] = {\n    // clang-format off\n    llvm::ARM::LDRD_PRE,\n    llvm::ARM::STRD_PRE,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_IMMORREG_4_SIZE =\n    sizeof(ADDR_REG_IMMORREG_4_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_IMMORREG_4_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_IMMORREG_FN(patch, dest, 3, 4, 5);\n}\n\n/* Address in a register with +/- imm8 << offset\n * =============================================\n * note : instruction also in getFixedOperandValue\n */\nRelocatableInst::UniquePtrVec ADDR_REG_IMMSHIFT_FN(const Patch &patch, Reg dest,\n                                                   unsigned operandOff1,\n                                                   unsigned operandOff2,\n                                                   unsigned shiftoffset) {\n\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const rword address = patch.metadata.address;\n\n  QBDI_REQUIRE_ABORT(operandOff1 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(operandOff2 < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff2).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  RegLLVM addrReg = inst.getOperand(operandOff1).getReg();\n  sword imm = inst.getOperand(operandOff2).getImm();\n\n  bool offsetIsSub = (((imm >> 8) & 1) == 1);\n  imm = (imm & 0xff) << shiftoffset;\n\n  RelocatableInst::UniquePtrVec reloc;\n\n  if (addrReg == llvm::ARM::PC) {\n    if (llvmcpu == CPUMode::Thumb) {\n      reloc.push_back(LoadImm::unique(dest, address + 4));\n    } else {\n      reloc.push_back(LoadImm::unique(dest, address + 8));\n    }\n    addrReg = dest;\n  }\n\n  if (offsetIsSub) {\n    imm *= -1;\n  }\n  reloc.push_back(Add(llvmcpu, dest, addrReg, imm));\n  return reloc;\n}\n\n// address base in 2nd operand + imm in 3rd (shift 1)\nconstexpr unsigned ADDR_REG_IMMSHIFT_2_SHIFT1_TABLE[] = {\n    // clang-format off\n    llvm::ARM::VLDRH,\n    llvm::ARM::VSTRH,\n    // unsigned imm5\n    llvm::ARM::tLDRHi,\n    llvm::ARM::tSTRHi,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_IMMSHIFT_2_SHIFT1_SIZE =\n    sizeof(ADDR_REG_IMMSHIFT_2_SHIFT1_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_IMMSHIFT_2_SHIFT1_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_IMMSHIFT_FN(patch, dest, 1, 2, 1);\n}\n\n// address base in 2nd operand + imm in 3rd (shift 2)\nconstexpr unsigned ADDR_REG_IMMSHIFT_2_SHIFT2_TABLE[] = {\n    // clang-format off\n    llvm::ARM::VLDRD,\n    llvm::ARM::VLDRS,\n    llvm::ARM::VSTRD,\n    llvm::ARM::VSTRS,\n    // unsigned imm8\n    llvm::ARM::t2LDREX,\n    // unsigned imm5\n    llvm::ARM::tLDRi,\n    llvm::ARM::tLDRspi,\n    llvm::ARM::tSTRi,\n    llvm::ARM::tSTRspi,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_IMMSHIFT_2_SHIFT2_SIZE =\n    sizeof(ADDR_REG_IMMSHIFT_2_SHIFT2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_IMMSHIFT_2_SHIFT2_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_IMMSHIFT_FN(patch, dest, 1, 2, 2);\n}\n\n// address base in 3rd operand + imm in 4th (shift 2)\nconstexpr unsigned ADDR_REG_IMMSHIFT_3_SHIFT2_TABLE[] = {\n    // clang-format off\n    // unsigned imm8\n    llvm::ARM::t2STREX,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_IMMSHIFT_3_SHIFT2_SIZE =\n    sizeof(ADDR_REG_IMMSHIFT_3_SHIFT2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_IMMSHIFT_3_SHIFT2_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_IMMSHIFT_FN(patch, dest, 2, 3, 2);\n}\n\n/* Address in implicit reg + OFFSET\n * ================================\n */\n\nRelocatableInst::UniquePtrVec\nADDR_REG_IMPLICIT_OFF_FN(const Patch &patch, Reg dest, Reg base, sword offset) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n\n  if (offset == 0) {\n    return conv_unique<RelocatableInst>(MovReg::unique(dest, base));\n  } else {\n    return Addc(llvmcpu, dest, base, offset, dest);\n  }\n}\n\n// address in SP\nconstexpr unsigned ADDR_REG_SP_TABLE[] = {\n    // clang-format off\n    llvm::ARM::tPOP,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_SP_SIZE =\n    sizeof(ADDR_REG_SP_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_SP_FN(const Patch &patch, Reg dest,\n                                             bool writeAccess) {\n  return ADDR_REG_IMPLICIT_OFF_FN(patch, dest, Reg(REG_SP), 0);\n}\n\n// address in SP - dynamic\nconstexpr unsigned ADDR_REG_SP_DYN_TABLE[] = {\n    // clang-format off\n    llvm::ARM::tPUSH,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_SP_DYN_SIZE =\n    sizeof(ADDR_REG_SP_DYN_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec ADDR_REG_SP_DYN_FN(const Patch &patch, Reg dest,\n                                                 bool writeAccess) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  if (writeAccess) {\n    return ADDR_REG_IMPLICIT_OFF_FN(patch, dest, Reg(REG_SP),\n                                    -getWriteSize(inst, llvmcpu));\n  } else {\n    return ADDR_REG_IMPLICIT_OFF_FN(patch, dest, Reg(REG_SP),\n                                    -getReadSize(inst, llvmcpu));\n  }\n}\n\n/* Address in implicit PC + OFFSET\n * ===============================\n */\n\nRelocatableInst::UniquePtrVec ADDR_REG_IMPLICIT_PC_OFF_FN(const Patch &patch,\n                                                          Reg dest,\n                                                          unsigned operandOff,\n                                                          bool PCAlign) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n  rword address = patch.metadata.address;\n\n  if (PCAlign) {\n    address &= (~3);\n    QBDI_REQUIRE_ABORT(address % 4 == 0, \"Bad align {}\", patch);\n  }\n\n  QBDI_REQUIRE_ABORT(operandOff < inst.getNumOperands(), \"Invalid operand {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(operandOff).isImm(),\n                     \"Unexpected operand type {}\", patch);\n  sword offset = inst.getOperand(operandOff).getImm();\n\n  if (llvmcpu == CPUMode::Thumb) {\n    return conv_unique<RelocatableInst>(\n        LoadImm::unique(dest, Constant(address + 4 + offset)));\n  } else {\n    return conv_unique<RelocatableInst>(\n        LoadImm::unique(dest, Constant(address + 8 + offset)));\n  }\n}\n\n// address in Align(PC, 4) + 2nd operand (imm)\nconstexpr unsigned ADDR_REG_ALIGNPC_OFF_2_TABLE[] = {\n    // clang-format off\n    llvm::ARM::t2LDRBpci,\n    llvm::ARM::t2LDRHpci,\n    llvm::ARM::t2LDRSBpci,\n    llvm::ARM::t2LDRSHpci,\n    llvm::ARM::t2LDRpci,\n    llvm::ARM::tLDRpci,\n    // clang-format on\n};\n\nconstexpr size_t ADDR_REG_ALIGNPC_OFF_2_SIZE =\n    sizeof(ADDR_REG_ALIGNPC_OFF_2_TABLE) / sizeof(unsigned);\n\nRelocatableInst::UniquePtrVec\nADDR_REG_ALIGNPC_OFF_2_FN(const Patch &patch, Reg dest, bool writeAccess) {\n  return ADDR_REG_IMPLICIT_PC_OFF_FN(patch, dest, 1, true);\n}\n\nstruct MemoryAccessInfoArray {\n  AddressGenFn *addrFn[28] = {};\n  uint8_t addrArr[llvm::ARM::INSTRUCTION_LIST_END] = {0};\n\n  constexpr void addData(size_t index, const unsigned insts[],\n                         const size_t instsSize, AddressGenFn *fn) {\n    addrFn[index] = fn;\n    for (size_t i = 0; i < instsSize; i++) {\n      addrArr[insts[i]] = index;\n    }\n  }\n\n  constexpr MemoryAccessInfoArray() {\n    for (size_t i = 0; i < sizeof(addrArr) / sizeof(uint8_t); i++) {\n      addrArr[i] = -1;\n    }\n\n    uint8_t index = 0;\n    addData(index++, ADDR_REG_1_TABLE, ADDR_REG_1_SIZE, ADDR_REG_1_FN);\n    addData(index++, ADDR_REG_2_TABLE, ADDR_REG_2_SIZE, ADDR_REG_2_FN);\n    addData(index++, ADDR_REG_3_TABLE, ADDR_REG_3_SIZE, ADDR_REG_3_FN);\n    addData(index++, ADDR_REG_4_TABLE, ADDR_REG_4_SIZE, ADDR_REG_4_FN);\n    addData(index++, ADDR_REG_5_TABLE, ADDR_REG_5_SIZE, ADDR_REG_5_FN);\n    addData(index++, ADDR_REG_6_TABLE, ADDR_REG_6_SIZE, ADDR_REG_6_FN);\n    addData(index++, ADDR_REG_1_PLUS4_TABLE, ADDR_REG_1_PLUS4_SIZE,\n            ADDR_REG_1_PLUS4_FN);\n    addData(index++, ADDR_REG_1_DYN_TABLE, ADDR_REG_1_DYN_SIZE,\n            ADDR_REG_1_DYN_FN);\n    addData(index++, ADDR_REG_1_DYN_PLUS4_TABLE, ADDR_REG_1_DYN_PLUS4_SIZE,\n            ADDR_REG_1_DYN_PLUS4_FN);\n    addData(index++, ADDR_REG_1_SIMM_2_TABLE, ADDR_REG_1_SIMM_2_SIZE,\n            ADDR_REG_1_SIMM_2_FN);\n    addData(index++, ADDR_REG_2_SIMM_3_TABLE, ADDR_REG_2_SIMM_3_SIZE,\n            ADDR_REG_2_SIMM_3_FN);\n    addData(index++, ADDR_REG_3_SIMM_4_TABLE, ADDR_REG_3_SIMM_4_SIZE,\n            ADDR_REG_3_SIMM_4_FN);\n    addData(index++, ADDR_REG_4_SIMM_5_TABLE, ADDR_REG_4_SIMM_5_SIZE,\n            ADDR_REG_4_SIMM_5_FN);\n    addData(index++, ADDR_REG_1_REG_2_TABLE, ADDR_REG_1_REG_2_SIZE,\n            ADDR_REG_1_REG_2_FN);\n    addData(index++, ADDR_REG_2_REG_3_TABLE, ADDR_REG_2_REG_3_SIZE,\n            ADDR_REG_2_REG_3_FN);\n    addData(index++, ADDR_REG_1_REGSHIFT1_2_TABLE, ADDR_REG_1_REGSHIFT1_2_SIZE,\n            ADDR_REG_1_REGSHIFT1_2_FN);\n    addData(index++, ADDR_REG_2_REGLSL_3_TABLE, ADDR_REG_2_REGLSL_3_SIZE,\n            ADDR_REG_2_REGLSL_3_FN);\n    addData(index++, ADDR_REG_2_REGSHIFT_3_TABLE, ADDR_REG_2_REGSHIFT_3_SIZE,\n            ADDR_REG_2_REGSHIFT_3_FN);\n    addData(index++, ADDR_REG_3_REGSHIFT_4_TABLE, ADDR_REG_3_REGSHIFT_4_SIZE,\n            ADDR_REG_3_REGSHIFT_4_FN);\n    addData(index++, ADDR_REG_IMMORREG_2_TABLE, ADDR_REG_IMMORREG_2_SIZE,\n            ADDR_REG_IMMORREG_2_FN);\n    addData(index++, ADDR_REG_IMMORREG_3_TABLE, ADDR_REG_IMMORREG_3_SIZE,\n            ADDR_REG_IMMORREG_3_FN);\n    addData(index++, ADDR_REG_IMMORREG_4_TABLE, ADDR_REG_IMMORREG_4_SIZE,\n            ADDR_REG_IMMORREG_4_FN);\n    addData(index++, ADDR_REG_IMMSHIFT_2_SHIFT1_TABLE,\n            ADDR_REG_IMMSHIFT_2_SHIFT1_SIZE, ADDR_REG_IMMSHIFT_2_SHIFT1_FN);\n    addData(index++, ADDR_REG_IMMSHIFT_2_SHIFT2_TABLE,\n            ADDR_REG_IMMSHIFT_2_SHIFT2_SIZE, ADDR_REG_IMMSHIFT_2_SHIFT2_FN);\n    addData(index++, ADDR_REG_IMMSHIFT_3_SHIFT2_TABLE,\n            ADDR_REG_IMMSHIFT_3_SHIFT2_SIZE, ADDR_REG_IMMSHIFT_3_SHIFT2_FN);\n    addData(index++, ADDR_REG_SP_TABLE, ADDR_REG_SP_SIZE, ADDR_REG_SP_FN);\n    addData(index++, ADDR_REG_SP_DYN_TABLE, ADDR_REG_SP_DYN_SIZE,\n            ADDR_REG_SP_DYN_FN);\n    addData(index++, ADDR_REG_ALIGNPC_OFF_2_TABLE, ADDR_REG_ALIGNPC_OFF_2_SIZE,\n            ADDR_REG_ALIGNPC_OFF_2_FN);\n  }\n};\n\nconstexpr MemoryAccessInfoArray memoryAccessInfo;\n#if CHECK_MEMORYACCESS_TABLE\n\nstruct AddressGenerator {\n  const unsigned *insts;\n  size_t nbInsts;\n  AddressGenFn *fn;\n};\n\nint checkTable() {\n  const std::vector<AddressGenerator> addrInfo = {\n      {ADDR_REG_1_TABLE, ADDR_REG_1_SIZE, ADDR_REG_1_FN},\n      {ADDR_REG_2_TABLE, ADDR_REG_2_SIZE, ADDR_REG_2_FN},\n      {ADDR_REG_3_TABLE, ADDR_REG_3_SIZE, ADDR_REG_3_FN},\n      {ADDR_REG_4_TABLE, ADDR_REG_4_SIZE, ADDR_REG_4_FN},\n      {ADDR_REG_5_TABLE, ADDR_REG_5_SIZE, ADDR_REG_5_FN},\n      {ADDR_REG_6_TABLE, ADDR_REG_6_SIZE, ADDR_REG_6_FN},\n      {ADDR_REG_1_PLUS4_TABLE, ADDR_REG_1_PLUS4_SIZE, ADDR_REG_1_PLUS4_FN},\n      {ADDR_REG_1_DYN_TABLE, ADDR_REG_1_DYN_SIZE, ADDR_REG_1_DYN_FN},\n      {ADDR_REG_1_DYN_PLUS4_TABLE, ADDR_REG_1_DYN_PLUS4_SIZE,\n       ADDR_REG_1_DYN_PLUS4_FN},\n      {ADDR_REG_1_SIMM_2_TABLE, ADDR_REG_1_SIMM_2_SIZE, ADDR_REG_1_SIMM_2_FN},\n      {ADDR_REG_2_SIMM_3_TABLE, ADDR_REG_2_SIMM_3_SIZE, ADDR_REG_2_SIMM_3_FN},\n      {ADDR_REG_3_SIMM_4_TABLE, ADDR_REG_3_SIMM_4_SIZE, ADDR_REG_3_SIMM_4_FN},\n      {ADDR_REG_4_SIMM_5_TABLE, ADDR_REG_4_SIMM_5_SIZE, ADDR_REG_4_SIMM_5_FN},\n      {ADDR_REG_1_REG_2_TABLE, ADDR_REG_1_REG_2_SIZE, ADDR_REG_1_REG_2_FN},\n      {ADDR_REG_2_REG_3_TABLE, ADDR_REG_2_REG_3_SIZE, ADDR_REG_2_REG_3_FN},\n      {ADDR_REG_1_REGSHIFT1_2_TABLE, ADDR_REG_1_REGSHIFT1_2_SIZE,\n       ADDR_REG_1_REGSHIFT1_2_FN},\n      {ADDR_REG_2_REGLSL_3_TABLE, ADDR_REG_2_REGLSL_3_SIZE,\n       ADDR_REG_2_REGLSL_3_FN},\n      {ADDR_REG_2_REGSHIFT_3_TABLE, ADDR_REG_2_REGSHIFT_3_SIZE,\n       ADDR_REG_2_REGSHIFT_3_FN},\n      {ADDR_REG_3_REGSHIFT_4_TABLE, ADDR_REG_3_REGSHIFT_4_SIZE,\n       ADDR_REG_3_REGSHIFT_4_FN},\n      {ADDR_REG_IMMORREG_2_TABLE, ADDR_REG_IMMORREG_2_SIZE,\n       ADDR_REG_IMMORREG_2_FN},\n      {ADDR_REG_IMMORREG_3_TABLE, ADDR_REG_IMMORREG_3_SIZE,\n       ADDR_REG_IMMORREG_3_FN},\n      {ADDR_REG_IMMORREG_4_TABLE, ADDR_REG_IMMORREG_4_SIZE,\n       ADDR_REG_IMMORREG_4_FN},\n      {ADDR_REG_IMMSHIFT_2_SHIFT1_TABLE, ADDR_REG_IMMSHIFT_2_SHIFT1_SIZE,\n       ADDR_REG_IMMSHIFT_2_SHIFT1_FN},\n      {ADDR_REG_IMMSHIFT_2_SHIFT2_TABLE, ADDR_REG_IMMSHIFT_2_SHIFT2_SIZE,\n       ADDR_REG_IMMSHIFT_2_SHIFT2_FN},\n      {ADDR_REG_IMMSHIFT_3_SHIFT2_TABLE, ADDR_REG_IMMSHIFT_3_SHIFT2_SIZE,\n       ADDR_REG_IMMSHIFT_3_SHIFT2_FN},\n      {ADDR_REG_SP_TABLE, ADDR_REG_SP_SIZE, ADDR_REG_SP_FN},\n      {ADDR_REG_SP_DYN_TABLE, ADDR_REG_SP_DYN_SIZE, ADDR_REG_SP_DYN_FN},\n      {ADDR_REG_ALIGNPC_OFF_2_TABLE, ADDR_REG_ALIGNPC_OFF_2_SIZE,\n       ADDR_REG_ALIGNPC_OFF_2_FN},\n  };\n\n  const LLVMCPUs llvmcpus{\"\", {}, Options::NO_OPT};\n  const LLVMCPU &llvmcpu = llvmcpus.getCPU(CPUMode::DEFAULT);\n  const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n\n  for (unsigned op = 0; op < llvm::ARM::INSTRUCTION_LIST_END; op++) {\n    llvm::MCInst inst;\n    inst.setOpcode(op);\n    const char *opcode = MCII.getName(inst.getOpcode()).data();\n\n    if (getReadSize(inst, llvmcpu) != 0 || getWriteSize(inst, llvmcpu) != 0) {\n      if (memoryAccessInfo.addrArr[op] == ((uint8_t)-1)) {\n        fprintf(stderr,\n                \"[MemoryAccessInfoArray checkTable] \"\n                \"opcode %s doesn't have an associated lambda\\n\",\n                opcode);\n        abort();\n      }\n      unsigned index = 0;\n      for (const auto &e : addrInfo) {\n        for (size_t i = 0; i < e.nbInsts; i++) {\n          unsigned o = e.insts[i];\n          if (o == op && memoryAccessInfo.addrArr[op] != index) {\n            fprintf(\n                stderr,\n                \"[MemoryAccessInfoArray checkTable] \"\n                \"opcode %s associated with lambda %d but expected lambda %d\\n\",\n                opcode, memoryAccessInfo.addrArr[op], index);\n            abort();\n          }\n          if (o == op &&\n              memoryAccessInfo.addrFn[memoryAccessInfo.addrArr[op]] != e.fn) {\n            fprintf(stderr,\n                    \"[MemoryAccessInfoArray checkTable] \"\n                    \"unexpected lambda association for opcode %s : %p != %p\\n\",\n                    opcode,\n                    memoryAccessInfo.addrFn[memoryAccessInfo.addrArr[op]],\n                    e.fn);\n            abort();\n          }\n        }\n        index++;\n      }\n    } else {\n      if (memoryAccessInfo.addrArr[op] != ((uint8_t)-1)) {\n        fprintf(stderr,\n                \"[MemoryAccessInfoArray checkTable] \"\n                \"opcode %s have an associated lambda but doesn't have an \"\n                \"associated memory access size\\n\",\n                opcode);\n        abort();\n      }\n    }\n  }\n  return 0;\n}\n\nint __check_debug = checkTable();\n#endif\n\n} // anonymous namespace\n\nRelocatableInst::UniquePtrVec generateAddressPatch(const Patch &patch,\n                                                   bool writeAccess, Reg dest) {\n  const LLVMCPU &llvmcpu = *patch.llvmcpu;\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  if (writeAccess) {\n    QBDI_REQUIRE(getWriteSize(inst, llvmcpu) != 0);\n  } else {\n    QBDI_REQUIRE(getReadSize(inst, llvmcpu) != 0);\n  }\n  const uint8_t index = memoryAccessInfo.addrArr[inst.getOpcode()];\n  QBDI_REQUIRE(index != ((uint8_t)-1));\n\n  return memoryAccessInfo.addrFn[index](patch, dest, writeAccess);\n}\n\n// Generate dynamic PatchGenerator for instruction\n// ===============================================\n\nnamespace {\n\nenum MemoryTag : uint16_t {\n  MEN_COND_REACH_TAG = MEMORY_TAG_BEGIN + 0,\n\n  MEM_READ_ADDRESS_TAG = MEMORY_TAG_BEGIN + 1,\n  MEM_WRITE_ADDRESS_TAG = MEMORY_TAG_BEGIN + 2,\n\n  MEM_READ_VALUE_TAG = MEMORY_TAG_BEGIN + 3,\n  MEM_WRITE_VALUE_TAG = MEMORY_TAG_BEGIN + 4,\n  MEM_VALUE_EXTENDED_TAG = MEMORY_TAG_BEGIN + 5,\n};\n\nconst PatchGenerator::UniquePtrVec &\ngenerateReadInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n  if (llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n        GetReadAddress::unique(Temp(0)),\n        WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n        SetCondReachAndJump::unique(Temp(0), Shadow(MEN_COND_REACH_TAG),\n                                    PatchGenerator::UniquePtrVec()));\n    return r;\n  }\n  switch (getReadSize(patch.metadata.inst, llvmcpu)) {\n    case 1:\n    case 2:\n    case 4: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  GetReadValue::unique(Temp(0), Temp(0), 0),\n                  WriteTemp::unique(Temp(0), Shadow(MEM_READ_VALUE_TAG)))));\n      return r;\n    }\n    case 3: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  GetReadValue::unique(Temp(1), Temp(0), 0),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)))));\n      return r;\n    }\n    case 6:\n    case 8: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  GetReadValue::unique(Temp(1), Temp(0), 0),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_READ_VALUE_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 1),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 12: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 2),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 16: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 20: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 4),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 24: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 28: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 6),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 32: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 36: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 8),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 40: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 44: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 10),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 48: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 52: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 12),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 56: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 60: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetReadValue::unique(Temp(1), Temp(0), 14),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 64: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(\n              Temp(1), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_READ_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 68:\n    case 72:\n    case 76:\n    case 80:\n    case 84:\n    case 88:\n    case 92:\n    case 96:\n    case 100:\n    case 104:\n    case 108:\n    case 112:\n    case 116:\n    case 120:\n    case 124:\n    case 128: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          SetCondReachAndJump::unique(Temp(0), Shadow(MEN_COND_REACH_TAG),\n                                      PatchGenerator::UniquePtrVec()));\n      return r;\n    }\n\n    default:\n      QBDI_ABORT(\"Unexpected number of memory Access {} {}\",\n                 getReadSize(patch.metadata.inst, llvmcpu), patch);\n  }\n}\n\nconst PatchGenerator::UniquePtrVec &\ngeneratePreWriteInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n      GetWrittenAddress::unique(Temp(0)),\n      WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)));\n  return r;\n}\n\nconst PatchGenerator::UniquePtrVec &\ngeneratePostWriteInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n  if (llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n        SetCondReachAndJump::unique(Temp(0), Shadow(MEN_COND_REACH_TAG),\n                                    PatchGenerator::UniquePtrVec()));\n    return r;\n  }\n  switch (getWriteSize(patch.metadata.inst, llvmcpu)) {\n    case 1:\n    case 2:\n    case 4: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  GetWrittenValue::unique(Temp(0), Temp(0), 0),\n                  WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_VALUE_TAG)))));\n      return r;\n    }\n    case 3: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 0),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)))));\n      return r;\n    }\n    case 6:\n    case 8: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 0),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_WRITE_VALUE_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 1),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 12: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 2),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 16: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 20: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 4),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 24: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 28: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 6),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 32: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 36: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 8),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 40: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 44: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 10),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 48: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 52: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 12),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 56: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 60: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  GetWrittenValue::unique(Temp(1), Temp(0), 14),\n                  WriteTemp::unique(Temp(1), Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 64: {\n      static const PatchGenerator::UniquePtrVec r =\n          conv_unique<PatchGenerator>(SetCondReachAndJump::unique(\n              Temp(0), Shadow(MEN_COND_REACH_TAG),\n              conv_unique<PatchGenerator>(\n                  ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_WRITE_VALUE_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)),\n                  BackupValueX2::unique(Temp(1), Temp(2), Temp(0),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG),\n                                        Shadow(MEM_VALUE_EXTENDED_TAG)))));\n      return r;\n    }\n    case 68:\n    case 72:\n    case 76:\n    case 80:\n    case 84:\n    case 88:\n    case 92:\n    case 96:\n    case 100:\n    case 104:\n    case 108:\n    case 112:\n    case 116:\n    case 120:\n    case 124:\n    case 128: {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          SetCondReachAndJump::unique(Temp(0), Shadow(MEN_COND_REACH_TAG),\n                                      PatchGenerator::UniquePtrVec()));\n      return r;\n    }\n\n    default:\n      QBDI_ABORT(\"Unexpected number of memory Access {} {}\",\n                 getWriteSize(patch.metadata.inst, llvmcpu), patch);\n  }\n}\n\n} // anonymous namespace\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessRead() {\n  return conv_unique<InstrRule>(InstrRuleDynamic::unique(\n      DoesReadAccess::unique(), generateReadInstrumentPatch, PREINST, false,\n      PRIORITY_MEMACCESS_LIMIT + 1, RelocTagPreInstMemAccess));\n}\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessWrite() {\n  return conv_unique<InstrRule>(\n      InstrRuleDynamic::unique(\n          DoesWriteAccess::unique(), generatePreWriteInstrumentPatch, PREINST,\n          false, PRIORITY_MEMACCESS_LIMIT, RelocTagPreInstMemAccess),\n      InstrRuleDynamic::unique(\n          DoesWriteAccess::unique(), generatePostWriteInstrumentPatch, POSTINST,\n          false, PRIORITY_MEMACCESS_LIMIT, RelocTagPostInstMemAccess));\n}\n\n// Analyse MemoryAccess from Shadow\n// ================================\n\nnamespace {\n\nvoid analyseMemoryAccessAddrValue(const ExecBlock &curExecBlock,\n                                  llvm::ArrayRef<ShadowInfo> &shadows,\n                                  std::vector<MemoryAccess> &dest,\n                                  const LLVMCPU &llvmcpu) {\n  if (shadows.size() < 1) {\n    return;\n  }\n\n  auto access = MemoryAccess();\n  access.flags = MEMORY_NO_FLAGS;\n\n  uint16_t expectValueTag;\n  const llvm::MCInst &inst = curExecBlock.getOriginalMCInst(shadows[0].instID);\n\n  switch (shadows[0].tag) {\n    default:\n      return;\n    case MEM_READ_ADDRESS_TAG:\n      access.type = MEMORY_READ;\n      access.size = getReadSize(inst, llvmcpu);\n      expectValueTag = MEM_READ_VALUE_TAG;\n      break;\n    case MEM_WRITE_ADDRESS_TAG:\n      access.type = MEMORY_WRITE;\n      access.size = getWriteSize(inst, llvmcpu);\n      expectValueTag = MEM_WRITE_VALUE_TAG;\n      break;\n  }\n\n  access.accessAddress = curExecBlock.getShadow(shadows[0].shadowID);\n  access.instAddress = curExecBlock.getInstAddress(shadows[0].instID);\n\n  if (access.size > 64 or\n      llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    access.value = 0;\n    access.flags |= MEMORY_UNKNOWN_VALUE;\n    // search if the shadow MEN_COND_REACH_TAG is present\n    // drop the access if the condition of the instruction isn't reached.\n    for (const ShadowInfo &info : shadows) {\n      if (shadows[0].instID != info.instID) {\n        break;\n      }\n      if (info.tag == MEN_COND_REACH_TAG) {\n        if (curExecBlock.getShadow(info.shadowID) != 1) {\n          return;\n        }\n        break;\n      }\n    }\n    dest.push_back(access);\n    return;\n  }\n\n  size_t index = 0;\n  // search the index of MEM_x_VALUE_TAG. For most instruction, it's the next\n  // shadow.\n  do {\n    index += 1;\n    if (index >= shadows.size()) {\n      QBDI_ERROR(\"Not found shadow tag {:x} for instruction {:x}\",\n                 expectValueTag, access.instAddress);\n      return;\n    }\n    QBDI_REQUIRE_ACTION(shadows[0].instID == shadows[index].instID, return);\n\n    // if the instruction is conditionnal and the condition hasn't be reach,\n    //  drop the shadows.\n    if (shadows[index].tag == MEN_COND_REACH_TAG and\n        curExecBlock.getShadow(shadows[index].shadowID) != 1)\n      return;\n  } while (shadows[index].tag != expectValueTag);\n\n  access.value = curExecBlock.getShadow(shadows[index].shadowID);\n\n  if (access.size < sizeof(rword)) {\n    rword mask = (1ull << (access.size * 8)) - 1;\n    access.value &= mask;\n  }\n\n  size_t extendShadow = 0;\n  size_t remindSize = access.size;\n\n  if (access.size > sizeof(rword)) {\n    extendShadow = (access.size / sizeof(rword));\n    if (access.size % sizeof(rword) == 0 && extendShadow > 0) {\n      --extendShadow;\n    }\n    access.size = sizeof(rword);\n    ++index;\n  }\n\n  dest.push_back(access);\n\n  for (; extendShadow > 0; --extendShadow, ++index) {\n    QBDI_REQUIRE_ACTION(index < shadows.size(), return);\n    QBDI_REQUIRE_ACTION(shadows[0].instID == shadows[index].instID, return);\n    QBDI_REQUIRE_ACTION(shadows[index].tag == MEM_VALUE_EXTENDED_TAG, return);\n\n    access.accessAddress += sizeof(rword);\n    access.value = curExecBlock.getShadow(shadows[index].shadowID);\n    remindSize -= sizeof(rword);\n    if (remindSize < sizeof(rword)) {\n      access.size = remindSize;\n      rword mask = (1ull << (access.size * 8)) - 1;\n      access.value &= mask;\n    }\n    dest.push_back(access);\n  }\n}\n\n} // anonymous namespace\n\nvoid analyseMemoryAccess(const ExecBlock &curExecBlock, uint16_t instID,\n                         bool afterInst, std::vector<MemoryAccess> &dest) {\n\n  llvm::ArrayRef<ShadowInfo> shadows = curExecBlock.getShadowByInst(instID);\n  const LLVMCPU &llvmcpu = curExecBlock.getLLVMCPUByInst(instID);\n  QBDI_DEBUG(\"Got {} shadows for Instruction {:x}\", shadows.size(), instID);\n\n  while (!shadows.empty()) {\n    QBDI_REQUIRE_ACTION(shadows[0].instID == instID, return);\n\n    switch (shadows[0].tag) {\n      default:\n        break;\n      case MEN_COND_REACH_TAG:\n        // if the instruction is conditionnal and the condition hasn't be reach,\n        //  drop the shadows.\n        if (curExecBlock.getShadow(shadows[0].shadowID) != 1)\n          return;\n        break;\n      case MEM_READ_ADDRESS_TAG:\n        analyseMemoryAccessAddrValue(curExecBlock, shadows, dest, llvmcpu);\n        break;\n      case MEM_WRITE_ADDRESS_TAG:\n        if (afterInst) {\n          analyseMemoryAccessAddrValue(curExecBlock, shadows, dest, llvmcpu);\n        }\n        break;\n    }\n    shadows = shadows.drop_front();\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/MemoryAccess_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCH_MEMORYACCESS_ARM_H\n#define PATCH_MEMORYACCESS_ARM_H\n\n#include <memory>\n\n#include \"Patch/MemoryAccess.h\"\n#include \"Patch/Patch.h\"\n\nnamespace QBDI {\nclass RelocatableInst;\nclass LLVMCPU;\n\n/* Generate the patch to retrive the address of the access from an instruction\n *\n * @param[in] patch          The current patch of the instruction\n * @param[in] writteneAccess If true, get the address of the written access\n *                           If false, get the address of the read access\n * @param[in] dest           The register to store the address\n *\n * Each instruction of InstInfo that does a memory access must have an\n * associated AddressGenFn\n */\nstd::vector<std::unique_ptr<RelocatableInst>>\ngenerateAddressPatch(const Patch &patch, bool writeAccess, Reg dest);\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/PatchCondition_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Target/ARM/Utils/ARMBaseInfo.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/ARM/PatchCondition_ARM.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nbool HasCond::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return patch.metadata.archMetadata.cond != llvm::ARMCC::AL;\n}\n\nbool OperandIs::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  if (position >= patch.metadata.inst.getNumOperands()) {\n    return false;\n  }\n  const llvm::MCOperand &op = patch.metadata.inst.getOperand(position);\n  if (type == RegType and op.isReg()) {\n    return reg == op.getReg();\n  } else if (type == ImmType and op.isImm()) {\n    return imm == op.getImm();\n  } else {\n    // OperandIs can be used before the check of the opcode\n    // If the operand doesn't have the good type, return false to skip the\n    // current Patchrules.\n    return false;\n  }\n}\n\nbool InITBlock::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return patch.metadata.archMetadata.posITblock > 0;\n}\n\nbool LastInITBlock::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return patch.metadata.archMetadata.posITblock == 1;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/PatchCondition_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHCONDITION_ARM_H\n#define PATCHCONDITION_ARM_H\n\n#include \"Patch/PatchCondition.h\"\n\nnamespace QBDI {\n\nclass HasCond : public AutoClone<PatchCondition, HasCond> {\n\npublic:\n  /*! Return true if the instruction has a condition and may be execute as a NOP\n   * depending of the flags\n   */\n  HasCond() {};\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass OperandIs : public AutoClone<PatchCondition, OperandIs> {\n  unsigned int position;\n  RegLLVM reg;\n  Constant imm;\n\n  enum { RegType, ImmType } type;\n\npublic:\n  /*! Return true if the operand is the expected register\n   */\n  OperandIs(unsigned int position, RegLLVM reg)\n      : position(position), reg(reg), imm(0), type(RegType) {};\n\n  /*! Return true if the operand is the expected immediate\n   */\n  OperandIs(unsigned int position, Constant imm)\n      : position(position), reg(0), imm(imm), type(ImmType) {};\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass InITBlock : public AutoClone<PatchCondition, InITBlock> {\n\npublic:\n  /*! Return true if the instruction is in a ITBlock\n   */\n  InITBlock() {};\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass LastInITBlock : public AutoClone<PatchCondition, LastInITBlock> {\n\npublic:\n  /*! Return true if the instruction is in a ITBlock and the last instruction of\n   * the block.\n   *\n   * Return false if the instruction is outside of an ITBlock, or inside but not\n   * the last instruction.\n   */\n  LastInITBlock() {};\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/PatchGenerator_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdint.h>\n#include <stdlib.h>\n#include <utility>\n\n#include \"Target/ARM/MCTargetDesc/ARMAddressingModes.h\"\n#include \"Target/ARM/Utils/ARMBaseInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/ARM/InstInfo_ARM.h\"\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"Patch/ARM/MemoryAccess_ARM.h\"\n#include \"Patch/ARM/PatchGenerator_ARM.h\"\n#include \"Patch/ARM/RelocatableInst_ARM.h\"\n#include \"Patch/ARM/TempManager_ARM.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/InstTransform.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\ntemplate <typename T>\nRelocatableInst::UniquePtrVec\nPureEval<T>::generate(const Patch &patch, TempManager &temp_manager) const {\n  return this->genReloc(*patch.llvmcpu);\n}\n\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, SetDataBlockAddress>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\n\n// Generic PatchGenerator that must be implemented by each target\n\n// TargetPrologue\n// ==============\n\nRelocatableInst::UniquePtrVec\nTargetPrologue::genReloc(const Patch &patch) const {\n\n  return {};\n}\n\n// JmpEpilogue\n// ===========\n\nRelocatableInst::UniquePtrVec\nJmpEpilogue::genReloc(const LLVMCPU &llvmcpu) const {\n\n  if (llvmcpu == CPUMode::ARM) {\n    return conv_unique<RelocatableInst>(EpilogueBranch::unique());\n  } else {\n    return conv_unique<RelocatableInst>(SetSREpilogue::unique(),\n                                        SRBranch::unique());\n  }\n}\n\n// Target Specific PatchGenerator\n\n// SetDataBlockAddress\n// ===================\n\nRelocatableInst::UniquePtrVec\nSetDataBlockAddress::genReloc(const LLVMCPU &llvmcpu) const {\n  CPUMode cpumode = llvmcpu.getCPUMode();\n\n  if (cpumode == CPUMode::Thumb) {\n    if (setScratchRegister) {\n      return conv_unique<RelocatableInst>(DataBlockAddress::unique());\n    } else {\n      return conv_unique<RelocatableInst>(DataBlockAddress::unique(reg));\n    }\n  } else {\n    QBDI_REQUIRE_ABORT(not setScratchRegister,\n                       \"ARM mode doesn't have a scratch register\");\n    return conv_unique<RelocatableInst>(DataBlockAddress::unique(reg),\n                                        NoReloc::unique(bic(reg, reg, 0xff)));\n  }\n}\n\n// WritePC\n// =======\n\nRelocatableInst::UniquePtrVec\nWritePC::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n\n  bool forceThumb = false;\n  bool forceARM = false;\n\n  switch (patch.metadata.inst.getOpcode()) {\n    // never (see BranchWritePC)\n    //  B, BL, CBNZ, CBZ, TBB, TBH\n    case llvm::ARM::BL:\n    case llvm::ARM::BL_pred:\n    case llvm::ARM::Bcc:\n    case llvm::ARM::t2B:\n    case llvm::ARM::t2BXAUT:\n    case llvm::ARM::t2Bcc:\n    case llvm::ARM::t2TBB:\n    case llvm::ARM::t2TBH:\n    case llvm::ARM::tB:\n    case llvm::ARM::tBL:\n    case llvm::ARM::tBcc:\n    case llvm::ARM::tCBNZ:\n    case llvm::ARM::tCBZ:\n      // the instrumentation process manages the address\n      // nothings to add here\n      break;\n    // since 5 (see LoadWritePC)\n    //  LDM<IA|DA|DB|IB>, LDR, POP\n    //\n    // The new address is loaded from memory:\n    //   need to patch it if < ARMv5\n    case llvm::ARM::LDMDA:\n    case llvm::ARM::LDMDA_UPD:\n    case llvm::ARM::LDMDB:\n    case llvm::ARM::LDMDB_UPD:\n    case llvm::ARM::LDMIA:\n    case llvm::ARM::LDMIA_UPD:\n    case llvm::ARM::LDMIB:\n    case llvm::ARM::LDMIB_UPD:\n    case llvm::ARM::LDR_POST_IMM:\n    case llvm::ARM::LDR_PRE_IMM:\n    case llvm::ARM::LDRi12:\n    case llvm::ARM::LDRrs:\n    case llvm::ARM::t2LDMDB:\n    case llvm::ARM::t2LDMDB_UPD:\n    case llvm::ARM::t2LDMIA:\n    case llvm::ARM::t2LDMIA_UPD:\n    case llvm::ARM::t2LDR_POST:\n    case llvm::ARM::t2LDR_PRE:\n    case llvm::ARM::t2LDRi12:\n    case llvm::ARM::t2LDRi8:\n    case llvm::ARM::t2LDRpci:\n    case llvm::ARM::t2LDRs:\n    case llvm::ARM::tPOP:\n      if (patch.llvmcpu->hasOptions(Options::OPT_ARMv4_bit)) {\n        if (cpumode == CPUMode::Thumb) {\n          forceThumb = true;\n        } else {\n          forceARM = true;\n        }\n      }\n      break;\n    // since 7 and current mode == ARM (see ALUWritePC)\n    //  ADC, ADD, AND, ASR, BIC, EOR, LSL, LSR, MOV, MVN, ORR, ROR, RRX, RSB,\n    //  RSC, SBC, SUB\n    case llvm::ARM::ADCri:\n    case llvm::ARM::ADCrr:\n    case llvm::ARM::ADCrsi:\n    case llvm::ARM::ADDri:\n    case llvm::ARM::ADDrr:\n    case llvm::ARM::ADDrsi:\n    case llvm::ARM::ANDri:\n    case llvm::ARM::ANDrr:\n    case llvm::ARM::ANDrsi:\n    case llvm::ARM::BICri:\n    case llvm::ARM::BICrr:\n    case llvm::ARM::BICrsi:\n    case llvm::ARM::EORri:\n    case llvm::ARM::EORrr:\n    case llvm::ARM::EORrsi:\n    case llvm::ARM::MOVi16:\n    case llvm::ARM::MOVi:\n    case llvm::ARM::MOVr:\n    case llvm::ARM::MOVPCLR:\n    case llvm::ARM::MOVsi:\n    case llvm::ARM::MVNi:\n    case llvm::ARM::MVNr:\n    case llvm::ARM::ORRri:\n    case llvm::ARM::ORRrr:\n    case llvm::ARM::ORRrsi:\n    case llvm::ARM::RSBri:\n    case llvm::ARM::RSBrr:\n    case llvm::ARM::RSBrsi:\n    case llvm::ARM::RSCri:\n    case llvm::ARM::RSCrr:\n    case llvm::ARM::RSCrsi:\n    case llvm::ARM::SBCri:\n    case llvm::ARM::SBCrr:\n    case llvm::ARM::SBCrsi:\n    case llvm::ARM::SUBri:\n    case llvm::ARM::SUBrr:\n    case llvm::ARM::SUBrsi:\n    case llvm::ARM::tADDhirr:\n    case llvm::ARM::tADDrSP:\n    case llvm::ARM::tMOVr:\n      if (cpumode == CPUMode::Thumb) {\n        forceThumb = true;\n      } else if (patch.llvmcpu->hasOptions(Options::OPT_ARMv5T_6)) {\n        forceARM = true;\n      }\n      break;\n    // always (see BXWritePC)\n    //  BLX, BX, BXJ\n    case llvm::ARM::BLX:\n    case llvm::ARM::BLX_pred:\n    case llvm::ARM::BX:\n    case llvm::ARM::BX_RET:\n    case llvm::ARM::BX_pred:\n    case llvm::ARM::tBLXr:\n      // register operand, depend on the value of the register\n      // do nothings\n      break;\n    case llvm::ARM::BLXi:\n    case llvm::ARM::tBLXi:\n      // managed by GetPCOffset\n      break;\n    case llvm::ARM::tBX:\n      // if BX pc, switch to ARM (but PC is always aligned, nothing to do)\n      // else, depend on the value of the register\n      break;\n    default:\n      QBDI_ABORT(\"SetExchange doesn't support this instruction: {}\", patch);\n  }\n\n  RelocatableInst::UniquePtrVec relocInstList;\n\n  QBDI_REQUIRE_ABORT((not forceThumb) or (not forceARM),\n                     \"Cannot force both ARM and Thumb mode at the same time {}\",\n                     patch);\n\n  if (forceThumb or forceARM) {\n    unsigned cond =\n        dropCond ? llvm::ARMCC::AL : patch.metadata.archMetadata.cond;\n\n    Reg tempReg = temp_manager.getRegForTemp(temp);\n\n    if (cpumode == CPUMode::Thumb) {\n      // we need to keep the condition, as we must not force the new address\n      // mode if the instruction isn't executed\n      if (cond != llvm::ARMCC::AL) {\n        append(relocInstList, ItPatch(false).generate(patch, temp_manager));\n      }\n      if (forceThumb) {\n        relocInstList.push_back(\n            NoReloc::unique(t2orri(tempReg, tempReg, 1, cond)));\n      } else if (forceARM) {\n        relocInstList.push_back(\n            NoReloc::unique(t2bic(tempReg, tempReg, 1, cond)));\n      }\n    } else {\n      if (forceThumb) {\n        relocInstList.push_back(\n            NoReloc::unique(orri(tempReg, tempReg, 1, cond)));\n      } else if (forceARM) {\n        relocInstList.push_back(\n            NoReloc::unique(bic(tempReg, tempReg, 1, cond)));\n      }\n    }\n  }\n\n  append(relocInstList,\n         WriteTemp(temp, Offset(Reg(REG_PC))).generate(patch, temp_manager));\n\n  return relocInstList;\n}\n\n// SetExchange\n// ===========\n\nRelocatableInst::UniquePtrVec\nSetExchange::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n\n  switch (patch.metadata.inst.getOpcode()) {\n    // never (see BranchWritePC)\n    //  B, BL, CBNZ, CBZ, TBB, TBH\n    case llvm::ARM::BL:\n    case llvm::ARM::BL_pred:\n    case llvm::ARM::Bcc:\n    case llvm::ARM::t2B:\n    case llvm::ARM::t2Bcc:\n    case llvm::ARM::t2TBB:\n    case llvm::ARM::t2TBH:\n    case llvm::ARM::tB:\n    case llvm::ARM::tBL:\n    case llvm::ARM::tBcc:\n    case llvm::ARM::tCBNZ:\n    case llvm::ARM::tCBZ:\n      return {};\n    // since 5 (see LoadWritePC)\n    //  LDM<IA|DA|DB|IB>, LDR, POP\n    case llvm::ARM::LDMDA:\n    case llvm::ARM::LDMDA_UPD:\n    case llvm::ARM::LDMDB:\n    case llvm::ARM::LDMDB_UPD:\n    case llvm::ARM::LDMIA:\n    case llvm::ARM::LDMIA_UPD:\n    case llvm::ARM::LDMIB:\n    case llvm::ARM::LDMIB_UPD:\n    case llvm::ARM::LDR_POST_IMM:\n    case llvm::ARM::LDR_PRE_IMM:\n    case llvm::ARM::LDRi12:\n    case llvm::ARM::LDRrs:\n    case llvm::ARM::t2LDMDB:\n    case llvm::ARM::t2LDMDB_UPD:\n    case llvm::ARM::t2LDMIA:\n    case llvm::ARM::t2LDMIA_UPD:\n    case llvm::ARM::t2LDR_POST:\n    case llvm::ARM::t2LDR_PRE:\n    case llvm::ARM::t2LDRi12:\n    case llvm::ARM::t2LDRi8:\n    case llvm::ARM::t2LDRpci:\n    case llvm::ARM::t2LDRs:\n    case llvm::ARM::tPOP:\n      if (patch.llvmcpu->hasOptions(Options::OPT_ARMv4_bit)) {\n        return {};\n      }\n      break;\n    // since 7 and current mode == ARM (see ALUWritePC)\n    //  ADC, ADD, AND, ASR, BIC, EOR, LSL, LSR, MOV, MVN, ORR, ROR, RRX, RSB,\n    //  RSC, SBC, SUB\n    case llvm::ARM::ADCri:\n    case llvm::ARM::ADCrr:\n    case llvm::ARM::ADCrsi:\n    case llvm::ARM::ADDri:\n    case llvm::ARM::ADDrr:\n    case llvm::ARM::ADDrsi:\n    case llvm::ARM::ANDri:\n    case llvm::ARM::ANDrr:\n    case llvm::ARM::ANDrsi:\n    case llvm::ARM::BICri:\n    case llvm::ARM::BICrr:\n    case llvm::ARM::BICrsi:\n    case llvm::ARM::EORri:\n    case llvm::ARM::EORrr:\n    case llvm::ARM::EORrsi:\n    case llvm::ARM::MOVi16:\n    case llvm::ARM::MOVi:\n    case llvm::ARM::MOVr:\n    case llvm::ARM::MOVPCLR:\n    case llvm::ARM::MOVsi:\n    case llvm::ARM::MVNi:\n    case llvm::ARM::MVNr:\n    case llvm::ARM::ORRri:\n    case llvm::ARM::ORRrr:\n    case llvm::ARM::ORRrsi:\n    case llvm::ARM::RSBri:\n    case llvm::ARM::RSBrr:\n    case llvm::ARM::RSBrsi:\n    case llvm::ARM::RSCri:\n    case llvm::ARM::RSCrr:\n    case llvm::ARM::RSCrsi:\n    case llvm::ARM::SBCri:\n    case llvm::ARM::SBCrr:\n    case llvm::ARM::SBCrsi:\n    case llvm::ARM::SUBri:\n    case llvm::ARM::SUBrr:\n    case llvm::ARM::SUBrsi:\n      if (patch.llvmcpu->hasOptions(Options::OPT_ARMv5T_6) or\n          cpumode != CPUMode::ARM) {\n        return {};\n      }\n      break;\n    case llvm::ARM::tADDrSP:\n    case llvm::ARM::tADDhirr:\n    case llvm::ARM::tMOVr:\n      // Thumb inst\n      return {};\n    // always (see BXWritePC)\n    //  BLX, BX, BXJ\n    case llvm::ARM::BLX:\n    case llvm::ARM::BLX_pred:\n    case llvm::ARM::BLXi:\n    case llvm::ARM::BX:\n    case llvm::ARM::BX_RET:\n    case llvm::ARM::BX_pred:\n    case llvm::ARM::t2BXAUT:\n    case llvm::ARM::tBLXi:\n    case llvm::ARM::tBLXr:\n    case llvm::ARM::tBX:\n      break;\n    default:\n      QBDI_ABORT(\"SetExchange doesn't support this instruction: {}\", patch);\n  }\n\n  Reg destReg = temp_manager.getRegForTemp(temp);\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n      cpumode == CPUMode::Thumb) {\n    return conv_unique<RelocatableInst>(\n        T2it(cpumode, patch.metadata.archMetadata.cond,\n             (unsigned)llvm::ARM::PredBlockMask::TT),\n        LoadImmCC::unique(destReg, Constant(1),\n                          patch.metadata.archMetadata.cond),\n        StoreDataBlockCC::unique(destReg,\n                                 Offset(offsetof(Context, hostState.exchange)),\n                                 patch.metadata.archMetadata.cond));\n  } else {\n    return conv_unique<RelocatableInst>(\n        LoadImmCC::unique(destReg, Constant(1),\n                          patch.metadata.archMetadata.cond),\n        StoreDataBlockCC::unique(destReg,\n                                 Offset(offsetof(Context, hostState.exchange)),\n                                 patch.metadata.archMetadata.cond));\n  }\n}\n\n// GetPCOffset\n// ===========\n\nRelocatableInst::UniquePtrVec\nGetPCOffset::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  RegLLVM destReg;\n  rword imm;\n\n  if (type == OpOperandType) {\n    QBDI_REQUIRE_ABORT(opDest < patch.metadata.inst.getNumOperands(),\n                       \"Invalid operand {}\", patch);\n    QBDI_REQUIRE_ABORT(patch.metadata.inst.getOperand(opDest).isReg(),\n                       \"Unexpected operand type {}\", patch);\n    destReg = patch.metadata.inst.getOperand(opDest).getReg();\n  } else {\n    destReg = temp_manager.getRegForTemp(temp);\n  }\n\n  if (type == TmpConstantType) {\n    imm = cst;\n  } else {\n    QBDI_REQUIRE_ABORT(type == TmpOperandType or type == OpOperandType,\n                       \"Unexepcted type {}\", patch);\n    QBDI_REQUIRE_ABORT(op < patch.metadata.inst.getNumOperands(),\n                       \"Invalid operand {}\", patch);\n    QBDI_REQUIRE_ABORT(patch.metadata.inst.getOperand(op).isImm(),\n                       \"Unexpected operand type {}\", patch);\n    imm = patch.metadata.inst.getOperand(op).getImm();\n    switch (patch.metadata.inst.getOpcode()) {\n      case llvm::ARM::BLXi:\n      case llvm::ARM::t2B:\n      case llvm::ARM::t2Bcc:\n      case llvm::ARM::tB:\n      case llvm::ARM::tBL:\n      case llvm::ARM::tBcc:\n      case llvm::ARM::tCBNZ:\n      case llvm::ARM::tCBZ:\n        // switch (or keep) the CPU to Thumb: set LSB to 1\n        imm |= 1;\n        break;\n      case llvm::ARM::tBLXi:\n        // switch to ARM mode : remove LSB if any\n        imm ^= (imm & 1);\n        break;\n      case llvm::ARM::tADR:\n        imm = imm << 2;\n        break;\n      default:\n        break;\n    }\n  }\n  if (cpumode == CPUMode::Thumb) {\n    imm = imm + patch.metadata.address + 4;\n    // instruction isn't align, need to detect instruction that used PC or\n    // Align(PC, 4)\n    if (patch.metadata.address % 4 != 0) {\n      QBDI_REQUIRE_ABORT(patch.metadata.address % 4 == 2,\n                         \"Not aligned instruction {}\", patch);\n      switch (patch.metadata.inst.getOpcode()) {\n        case llvm::ARM::VLDRD:\n        case llvm::ARM::VLDRH:\n        case llvm::ARM::VLDRS:\n        case llvm::ARM::t2ADR:\n        case llvm::ARM::t2LDC2L_OFFSET:\n        case llvm::ARM::t2LDC2_OFFSET:\n        case llvm::ARM::t2LDCL_OFFSET:\n        case llvm::ARM::t2LDC_OFFSET:\n        case llvm::ARM::t2LDRBpci:\n        case llvm::ARM::t2LDRDi8:\n        case llvm::ARM::t2LDRHpci:\n        case llvm::ARM::t2LDRSBpci:\n        case llvm::ARM::t2LDRSHpci:\n        case llvm::ARM::t2LDRpci:\n        case llvm::ARM::tADR:\n        case llvm::ARM::tBLXi:\n        case llvm::ARM::tLDRpci:\n          imm -= 2;\n          break;\n        case llvm::ARM::t2B:\n        case llvm::ARM::t2Bcc:\n        case llvm::ARM::t2TBB:\n        case llvm::ARM::t2TBH:\n        case llvm::ARM::tADDhirr:\n        case llvm::ARM::tADDrSP:\n        case llvm::ARM::tADDspr:\n        case llvm::ARM::tB:\n        case llvm::ARM::tBL:\n        case llvm::ARM::tBLXr:\n        case llvm::ARM::tBcc:\n        case llvm::ARM::tCBZ:\n        case llvm::ARM::tCBNZ:\n        case llvm::ARM::tMOVr:\n          break;\n        case llvm::ARM::tBX:\n          QBDI_ABORT(\"BX pc with PC not aligned: {}\", patch);\n        default:\n          QBDI_ABORT(\"Missing PC align behavior for: {}\", patch);\n      }\n    }\n  } else {\n    imm = imm + patch.metadata.address + 8;\n  }\n  if (keepCond) {\n    if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n        cpumode == CPUMode::Thumb) {\n      return conv_unique<RelocatableInst>(\n          T2it(cpumode, patch.metadata.archMetadata.cond,\n               (unsigned)llvm::ARM::PredBlockMask::T),\n          LoadImmCC::unique(destReg, Constant(imm),\n                            patch.metadata.archMetadata.cond));\n    } else {\n      return conv_unique<RelocatableInst>(LoadImmCC::unique(\n          destReg, Constant(imm), patch.metadata.archMetadata.cond));\n    }\n  } else {\n    return conv_unique<RelocatableInst>(\n        LoadImm::unique(destReg, Constant(imm)));\n  }\n\n  _QBDI_UNREACHABLE();\n}\n\n// GetNextInstAddr\n// ===============\n\nRelocatableInst::UniquePtrVec\nGetNextInstAddr::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  Reg destReg(0);\n  if (type == TmpType) {\n    destReg = temp_manager.getRegForTemp(temp);\n  } else {\n    destReg = reg;\n  }\n  rword imm = patch.metadata.endAddress();\n\n  if (cpumode == CPUMode::Thumb) {\n    imm |= 1;\n  }\n\n  if (keepCond) {\n    uint8_t cond = patch.metadata.archMetadata.cond;\n    if (invCond) {\n      if (cond == llvm::ARMCC::AL) {\n        // inv(AL) == not True == False\n        // In this case, do not generate an instruction\n        return {};\n      }\n      cond = llvm::ARMCC::getOppositeCondition((llvm::ARMCC::CondCodes)cond);\n    }\n    if (cond != llvm::ARMCC::AL and cpumode == CPUMode::Thumb) {\n      return conv_unique<RelocatableInst>(\n          T2it(cpumode, cond, (unsigned)llvm::ARM::PredBlockMask::T),\n          LoadImmCC::unique(destReg, Constant(imm), cond));\n    } else {\n      return conv_unique<RelocatableInst>(\n          LoadImmCC::unique(destReg, Constant(imm), cond));\n    }\n  } else {\n    return conv_unique<RelocatableInst>(\n        LoadImm::unique(destReg, Constant(imm)));\n  }\n\n  _QBDI_UNREACHABLE();\n}\n\n// GetOperandCC\n// ============\n\nRelocatableInst::UniquePtrVec\nGetOperandCC::generate(const Patch &patch, TempManager &temp_manager) const {\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  const llvm::MCInst &inst = patch.metadata.inst;\n  Reg destReg = reg;\n  if (type == TmpType) {\n    destReg = temp_manager.getRegForTemp(temp);\n  }\n  QBDI_REQUIRE_ABORT(op < inst.getNumOperands(), \"Invalid operand {}\", patch);\n  if (inst.getOperand(op).isReg()) {\n    if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n        cpumode == CPUMode::Thumb) {\n      return conv_unique<RelocatableInst>(\n          T2it(cpumode, patch.metadata.archMetadata.cond,\n               (unsigned)llvm::ARM::PredBlockMask::T),\n          MovRegCC::unique(destReg, inst.getOperand(op).getReg(),\n                           patch.metadata.archMetadata.cond));\n    } else {\n      return conv_unique<RelocatableInst>(\n          MovRegCC::unique(destReg, inst.getOperand(op).getReg(),\n                           patch.metadata.archMetadata.cond));\n    }\n  } else if (inst.getOperand(op).isImm()) {\n    if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n        cpumode == CPUMode::Thumb) {\n      return conv_unique<RelocatableInst>(\n          T2it(cpumode, patch.metadata.archMetadata.cond,\n               (unsigned)llvm::ARM::PredBlockMask::T),\n          LoadImmCC::unique(destReg, Constant(inst.getOperand(op).getImm()),\n                            patch.metadata.archMetadata.cond));\n    } else {\n      return conv_unique<RelocatableInst>(\n          LoadImmCC::unique(destReg, Constant(inst.getOperand(op).getImm()),\n                            patch.metadata.archMetadata.cond));\n    }\n  } else {\n    QBDI_ERROR(\"Invalid operand type for GetOperand()\");\n    return {};\n  }\n}\n\n// CopyRegCC\n// =========\n\nRelocatableInst::UniquePtrVec\nCopyRegCC::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  Reg dest = destReg;\n  if (type == Reg2Temp) {\n    dest = temp_manager.getRegForTemp(destTemp);\n  }\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n      cpumode == CPUMode::Thumb) {\n    return conv_unique<RelocatableInst>(\n        T2it(cpumode, patch.metadata.archMetadata.cond,\n             (unsigned)llvm::ARM::PredBlockMask::T),\n        MovRegCC::unique(dest, src, patch.metadata.archMetadata.cond));\n  } else {\n    return conv_unique<RelocatableInst>(\n        MovRegCC::unique(dest, src, patch.metadata.archMetadata.cond));\n  }\n}\n\n// WriteTempCC\n// ===========\n\nRelocatableInst::UniquePtrVec\nWriteTempCC::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n      cpumode == CPUMode::Thumb) {\n    return conv_unique<RelocatableInst>(\n        T2it(cpumode, patch.metadata.archMetadata.cond,\n             (unsigned)llvm::ARM::PredBlockMask::T),\n        StoreDataBlockCC::unique(temp_manager.getRegForTemp(temp), offset,\n                                 patch.metadata.archMetadata.cond));\n  } else {\n    return conv_unique<RelocatableInst>(\n        StoreDataBlockCC::unique(temp_manager.getRegForTemp(temp), offset,\n                                 patch.metadata.archMetadata.cond));\n  }\n}\n\n// WriteOperandCC\n// ==============\n\nRelocatableInst::UniquePtrVec\nWriteOperandCC::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n\n  QBDI_REQUIRE_ABORT(op < inst.getNumOperands(), \"Invalid operand {} {}\", op,\n                     patch);\n  if (inst.getOperand(op).isReg()) {\n    if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n        cpumode == CPUMode::Thumb) {\n      return conv_unique<RelocatableInst>(\n          T2it(cpumode, patch.metadata.archMetadata.cond,\n               (unsigned)llvm::ARM::PredBlockMask::T),\n          StoreDataBlockCC::unique(inst.getOperand(op).getReg(), offset,\n                                   patch.metadata.archMetadata.cond));\n    } else {\n      return conv_unique<RelocatableInst>(\n          StoreDataBlockCC::unique(inst.getOperand(op).getReg(), offset,\n                                   patch.metadata.archMetadata.cond));\n    }\n  } else {\n    QBDI_ERROR(\"Invalid operand type for WriteOperand()\");\n    return {};\n  }\n}\n\n// CopyTempCC\n// ==========\n\nRelocatableInst::UniquePtrVec\nCopyTempCC::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  Reg dest = destReg;\n  if (type == Temp2Temp) {\n    dest = temp_manager.getRegForTemp(destTemp);\n  }\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL and\n      cpumode == CPUMode::Thumb) {\n    return conv_unique<RelocatableInst>(\n        T2it(cpumode, patch.metadata.archMetadata.cond,\n             (unsigned)llvm::ARM::PredBlockMask::T),\n        MovRegCC::unique(dest, temp_manager.getRegForTemp(src),\n                         patch.metadata.archMetadata.cond));\n  } else {\n    return conv_unique<RelocatableInst>(\n        MovRegCC::unique(dest, temp_manager.getRegForTemp(src),\n                         patch.metadata.archMetadata.cond));\n  }\n}\n\n// AddOperandToTemp\n// ================\n\nRelocatableInst::UniquePtrVec\nAddOperandToTemp::generate(const Patch &patch,\n                           TempManager &temp_manager) const {\n\n  QBDI_REQUIRE_ABORT(*patch.llvmcpu == CPUMode::Thumb,\n                     \"Unimplemented in ARM mode {}\", patch);\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  Reg dest = temp_manager.getRegForTemp(temp);\n\n  QBDI_REQUIRE_ABORT(op < inst.getNumOperands(), \"Invalid operand {}\", patch);\n  QBDI_REQUIRE_ABORT(op2 < inst.getNumOperands(), \"Invalid operand {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(op).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(op2).isImm(), \"Unexpected operand type {}\",\n                     patch);\n\n  RegLLVM addrReg = inst.getOperand(op).getReg();\n  rword imm = inst.getOperand(op2).getImm();\n\n  switch (inst.getOpcode()) {\n    case llvm::ARM::t2LDREX:\n      imm = imm << 2;\n      break;\n    default:\n      break;\n  }\n\n  if (imm == 0) {\n    return conv_unique<RelocatableInst>(MovReg::unique(dest, addrReg));\n  } else {\n    return conv_unique<RelocatableInst>(\n        NoReloc::unique(t2add(dest, addrReg, imm)));\n  }\n}\n\n// LDMPatchGen\n// ===========\n\nRelocatableInst::UniquePtrVec\nLDMPatchGen::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::ARM, \"Only available in ARM mode {}\",\n                     patch);\n\n  // lock tempRegister. Only 1 register may be a temp register\n  Reg tempReg = temp_manager.getRegForTemp(temp);\n  temp_manager.lockTempManager();\n  QBDI_REQUIRE_ABORT(temp_manager.getUsedRegisters().size() == 1,\n                     \"Unexpected TempManager state {}\", patch);\n\n  // verify the tempReg isn't the address register (should never happend)\n  QBDI_REQUIRE_ABORT(0 < inst.getNumOperands(), \"Invalid instruction {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  RegLLVM addrReg = inst.getOperand(0).getReg();\n  QBDI_REQUIRE_ABORT(tempReg != addrReg, \"tempRegister allocation error {}\",\n                     patch);\n\n  // verify PC is on the register list (always the last register of the list)\n  QBDI_REQUIRE_ABORT(inst.getOperand(inst.getNumOperands() - 1).isReg() and\n                         inst.getOperand(inst.getNumOperands() - 1).getReg() ==\n                             GPR_ID[REG_PC],\n                     \"LDM without PC doesn't need this patch {}\", patch);\n\n  // verify if the tempReg is a register on the register list\n  // (may happend if all register are present in the register list)\n  bool tempIsNeeded =\n      (patch.regUsage[tempReg.getID()] & RegisterUsage::RegisterBoth) != 0;\n\n  // copy of the original MCInst but without PC (that is the last operand)\n  llvm::MCInst instWithoutPC = inst;\n  instWithoutPC.erase(instWithoutPC.begin() +\n                      (instWithoutPC.getNumOperands() - 1));\n\n  // copy of the original MCInst but without any register\n  // Only used with writeback instruction\n  llvm::MCInst instNoReg = inst;\n  // Verify if the address register is within the register list\n  bool addrIsSet = false;\n  while (instNoReg.getNumOperands() > 0 and\n         instNoReg.getOperand(instNoReg.getNumOperands() - 1).isReg() and\n         instNoReg.getOperand(instNoReg.getNumOperands() - 1).getReg() !=\n             llvm::ARM::CPSR and\n         instNoReg.getOperand(instNoReg.getNumOperands() - 1).getReg() !=\n             llvm::ARM::NoRegister) {\n    addrIsSet |=\n        (instNoReg.getOperand(instNoReg.getNumOperands() - 1).getReg() ==\n         instNoReg.getOperand(0).getReg());\n    instNoReg.erase(instNoReg.begin() + (instNoReg.getNumOperands() - 1));\n  }\n\n  // verify if the instruction is conditionnal\n  bool hasCond = patch.metadata.archMetadata.cond != llvm::ARMCC::AL;\n\n  RelocatableInst::UniquePtrVec res;\n\n  switch (inst.getOpcode()) {\n    case llvm::ARM::LDMIA: {\n      unsigned nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n\n      // if the instruction is conditionnal, load PC+4\n      if (hasCond) {\n        append(res, GetNextInstAddr(temp, /* keepCond */ false)\n                        .generate(patch, temp_manager));\n      }\n      // load PC (with cond if needed)\n      res.push_back(NoReloc::unique(ldri12(tempReg, addrReg, 4 * nbReg,\n                                           patch.metadata.archMetadata.cond)));\n\n      // fix and store PC (without cond)\n      append(res, WritePC(temp).generate(patch, temp_manager));\n\n      if (nbReg > 0) {\n        // apply the instruction without PC\n        res.push_back(NoReloc::unique(std::move(instWithoutPC)));\n\n        // if needed, replace the value of the tempRegister in the Datablock\n        if (tempIsNeeded) {\n          res.push_back(StoreDataBlockCC::unique(\n              tempReg, Offset(tempReg), patch.metadata.archMetadata.cond));\n        }\n      }\n\n      return res;\n    }\n    case llvm::ARM::LDMIB: {\n      unsigned nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n\n      // if the instruction is conditionnal, load PC+4\n      if (hasCond) {\n        append(res, GetNextInstAddr(temp, /* keepCond */ false)\n                        .generate(patch, temp_manager));\n      }\n      // load PC (with cond if needed)\n      res.push_back(NoReloc::unique(ldri12(tempReg, addrReg, 4 * nbReg + 4,\n                                           patch.metadata.archMetadata.cond)));\n\n      // fix and store PC (without cond)\n      append(res, WritePC(temp).generate(patch, temp_manager));\n\n      if (nbReg > 0) {\n        // apply the instruction without PC\n        res.push_back(NoReloc::unique(std::move(instWithoutPC)));\n\n        // if needed, replace the value of the tempRegister in the Datablock\n        if (tempIsNeeded) {\n          res.push_back(StoreDataBlockCC::unique(\n              tempReg, Offset(tempReg), patch.metadata.archMetadata.cond));\n        }\n      }\n\n      return res;\n    }\n    case llvm::ARM::LDMDA: {\n      unsigned nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n\n      // if the instruction is conditionnal, load PC+4\n      if (hasCond) {\n        append(res, GetNextInstAddr(temp, /* keepCond */ false)\n                        .generate(patch, temp_manager));\n      }\n      // load PC (with cond if needed)\n      res.push_back(NoReloc::unique(\n          ldri12(tempReg, addrReg, 0, patch.metadata.archMetadata.cond)));\n\n      // fix and store PC (without cond)\n      append(res, WritePC(temp).generate(patch, temp_manager));\n\n      if (nbReg > 0) {\n\n        // apply the instruction without PC\n        // Replace opcode by LDMDB to avoid PC position\n        instWithoutPC.setOpcode(llvm::ARM::LDMDB);\n        res.push_back(NoReloc::unique(std::move(instWithoutPC)));\n\n        // if needed, replace the value of the tempRegister in the Datablock\n        if (tempIsNeeded) {\n          res.push_back(StoreDataBlockCC::unique(\n              tempReg, Offset(tempReg), patch.metadata.archMetadata.cond));\n        }\n      }\n      return res;\n    }\n    case llvm::ARM::LDMDB: {\n      unsigned nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n\n      // if the instruction is conditionnal, load PC+4\n      if (hasCond) {\n        append(res, GetNextInstAddr(temp, /* keepCond */ false)\n                        .generate(patch, temp_manager));\n      }\n      // load PC (with cond if needed\n      res.push_back(NoReloc::unique(\n          ldri12(tempReg, addrReg, -4, patch.metadata.archMetadata.cond)));\n\n      // fix and store PC (without cond)\n      append(res, WritePC(temp).generate(patch, temp_manager));\n\n      if (nbReg > 0) {\n        // sub 4 to addrReg to avoid PC\n        res.push_back(NoReloc::unique(\n            sub(addrReg, addrReg, 4, patch.metadata.archMetadata.cond)));\n\n        // apply the instruction without PC\n        res.push_back(NoReloc::unique(std::move(instWithoutPC)));\n\n        if (not addrIsSet) {\n          // add 4 to addrReg\n          res.push_back(NoReloc::unique(\n              add(addrReg, addrReg, 4, patch.metadata.archMetadata.cond)));\n        }\n\n        // if needed, replace the value of the tempRegister in the Datablock\n        if (tempIsNeeded) {\n          res.push_back(StoreDataBlockCC::unique(\n              tempReg, Offset(tempReg), patch.metadata.archMetadata.cond));\n        }\n      }\n      return res;\n    }\n    case llvm::ARM::LDMIA_UPD:\n    case llvm::ARM::LDMIB_UPD: {\n      QBDI_REQUIRE_ABORT(\n          not addrIsSet,\n          \"invalid instruction (wback && registers<n> == '1') {}\", patch);\n\n      unsigned nbReg =\n          inst.getNumOperands() - /* source + wback + 2 * cond + PC*/ 5;\n\n      if (nbReg > 0) {\n        // apply the instruction without PC\n        res.push_back(NoReloc::unique(std::move(instWithoutPC)));\n\n        // if needed, replace the value of the tempRegister in the Datablock\n        if (tempIsNeeded) {\n          res.push_back(StoreDataBlockCC::unique(\n              tempReg, Offset(tempReg), patch.metadata.archMetadata.cond));\n        }\n      }\n\n      // if the instruction is conditionnal, load PC+4\n      if (hasCond) {\n        append(res, GetNextInstAddr(temp, /* keepCond */ false)\n                        .generate(patch, temp_manager));\n      }\n\n      // load PC (with instNoReg + tempReg) with the same instruction to wback\n      // the address\n      QBDI_REQUIRE_ABORT(instNoReg.getNumOperands() == 4, \"Unexpected state {}\",\n                         patch);\n      instNoReg.insert(instNoReg.end(),\n                       llvm::MCOperand::createReg(tempReg.getValue()));\n      res.push_back(NoReloc::unique(std::move(instNoReg)));\n\n      // fix and store PC (without cond)\n      append(res, WritePC(temp).generate(patch, temp_manager));\n\n      return res;\n    }\n    case llvm::ARM::LDMDA_UPD:\n    case llvm::ARM::LDMDB_UPD: {\n      QBDI_REQUIRE_ABORT(\n          not addrIsSet,\n          \"invalid instruction (wback && registers<n> == '1') {}\", patch);\n      unsigned nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 5;\n\n      // if the instruction is conditionnal, load PC+4\n      if (hasCond) {\n        append(res, GetNextInstAddr(temp, /* keepCond */ false)\n                        .generate(patch, temp_manager));\n      }\n\n      // load PC (with instNoReg + tempReg) with the same instruction to wback\n      // the address\n      QBDI_REQUIRE_ABORT(instNoReg.getNumOperands() == 4, \"Unexpected state {}\",\n                         patch);\n      instNoReg.insert(instNoReg.end(),\n                       llvm::MCOperand::createReg(tempReg.getValue()));\n      res.push_back(NoReloc::unique(std::move(instNoReg)));\n\n      // fix and store PC (without cond)\n      append(res, WritePC(temp).generate(patch, temp_manager));\n\n      if (nbReg > 0) {\n        // apply the instruction without PC\n        res.push_back(NoReloc::unique(std::move(instWithoutPC)));\n\n        // if needed, replace the value of the tempRegister in the Datablock\n        if (tempIsNeeded) {\n          res.push_back(StoreDataBlockCC::unique(\n              tempReg, Offset(tempReg), patch.metadata.archMetadata.cond));\n        }\n      }\n      return res;\n    }\n    default:\n      QBDI_ABORT(\"LDMPatchGen should not be used for this instruction: {}\",\n                 patch);\n  }\n}\n\n// STMPatchGen\n// ===========\n\nRelocatableInst::UniquePtrVec\nSTMPatchGen::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::ARM, \"Only available in ARM mode {}\",\n                     patch);\n\n  // lock tempRegister. Only 1 register may be a temp register\n  Reg tempReg = temp_manager.getRegForTemp(temp);\n  temp_manager.lockTempManager();\n  QBDI_REQUIRE_ABORT(temp_manager.getUsedRegisters().size() == 1,\n                     \"Unexpected TempManager state {}\", patch);\n\n  // verify the tempReg isn't the address register (should never happend)\n  QBDI_REQUIRE_ABORT(0 < inst.getNumOperands(), \"Invalid instruction {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  RegLLVM addrReg = inst.getOperand(0).getReg();\n  QBDI_REQUIRE_ABORT(tempReg != addrReg, \"tempRegister allocation error {}\",\n                     patch);\n\n  // verify PC is on the register list (always the last register of the list)\n  QBDI_REQUIRE_ABORT(inst.getOperand(inst.getNumOperands() - 1).isReg() and\n                         inst.getOperand(inst.getNumOperands() - 1).getReg() ==\n                             GPR_ID[REG_PC],\n                     \"STM without PC doesn't need this patch {}\", patch);\n\n  // verify if the tempReg is a register on the register list\n  // (may happend if all register are present in the register list)\n  bool tempIsNeeded =\n      (patch.regUsage[tempReg.getID()] & RegisterUsage::RegisterBoth) != 0;\n\n  unsigned nbReg;\n  unsigned fixPCOffset;\n\n  switch (inst.getOpcode()) {\n    case llvm::ARM::STMIA:\n      nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n      fixPCOffset = 4 * nbReg;\n      break;\n    case llvm::ARM::STMIB:\n      nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n      fixPCOffset = 4 * nbReg + 4;\n      break;\n    case llvm::ARM::STMDA:\n      nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n      fixPCOffset = 0;\n      break;\n    case llvm::ARM::STMDB:\n      nbReg = inst.getNumOperands() - /* source + 2 * cond + PC*/ 4;\n      fixPCOffset = -4;\n      break;\n    case llvm::ARM::STMIA_UPD:\n      nbReg = inst.getNumOperands() - /* source + wback + 2 * cond + PC*/ 5;\n      fixPCOffset = -4;\n      break;\n    case llvm::ARM::STMIB_UPD:\n      nbReg = inst.getNumOperands() - /* source + wback + 2 * cond + PC*/ 5;\n      fixPCOffset = 0;\n      break;\n    case llvm::ARM::STMDA_UPD:\n      nbReg = inst.getNumOperands() - /* source + wback + 2 * cond + PC*/ 5;\n      fixPCOffset = 4 * nbReg + 4;\n      break;\n    case llvm::ARM::STMDB_UPD:\n      nbReg = inst.getNumOperands() - /* source + wback + 2 * cond + PC*/ 5;\n      fixPCOffset = 4 * nbReg;\n      break;\n    default:\n      QBDI_ABORT(\"STMPatchGen should not be used for this instruction: {}\",\n                 patch);\n  }\n\n  RelocatableInst::UniquePtrVec res;\n\n  // if needed, load the value of the tempRegister from the Datablock\n  if (tempIsNeeded) {\n    res.push_back(LoadDataBlockCC::unique(tempReg, Offset(tempReg),\n                                          patch.metadata.archMetadata.cond));\n  }\n\n  // apply the instruction (the value of PC will be fixed after)\n  llvm::MCInst instCpy = inst;\n  res.push_back(NoReloc::unique(std::move(instCpy)));\n\n  // Get current PC\n  append(res, GetPCOffset(temp, Constant(0), /* keepCond */ false)\n                  .generate(patch, temp_manager));\n\n  // store PC (with cond if needed)\n  res.push_back(NoReloc::unique(\n      stri12(tempReg, addrReg, fixPCOffset, patch.metadata.archMetadata.cond)));\n\n  return res;\n}\n\n// GetReadAddress\n// ==============\n\nRelocatableInst::UniquePtrVec\nGetReadAddress::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n  return generateAddressPatch(patch, false, tmpRegister);\n}\n\n// GetWrittenAddress\n// =================\n\nRelocatableInst::UniquePtrVec\nGetWrittenAddress::generate(const Patch &patch,\n                            TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n  return generateAddressPatch(patch, true, tmpRegister);\n}\n\n// GetReadValue\n// ============\n\nRelocatableInst::UniquePtrVec\nGetReadValue::generate(const Patch &patch, TempManager &temp_manager) const {\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    if (index == 0) {\n      return conv_unique<RelocatableInst>(LoadImm::unique(tmpRegister, 0));\n    } else {\n      return {};\n    }\n  }\n\n  Reg addrRegister = temp_manager.getRegForTemp(addr);\n\n  bool isARM = (patch.llvmcpu->getCPUMode() == CPUMode::ARM);\n  bool wback = (tmpRegister != addrRegister);\n\n  unsigned readSize = getReadSize(patch.metadata.inst, *patch.llvmcpu);\n  switch (readSize) {\n    case 6:\n      if (index != 1) {\n        readSize = 4;\n      } else {\n        readSize = 2;\n      }\n    default:\n      break;\n  }\n  switch (readSize) {\n    case 1: {\n      if (isARM) {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrbPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrb(tmpRegister, addrRegister, 0)));\n        }\n      } else {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrbPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrb(tmpRegister, addrRegister, 0)));\n        }\n      }\n    }\n    case 2: {\n      if (isARM) {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrhPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrh(tmpRegister, addrRegister, 0)));\n        }\n      } else {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrhPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrh(tmpRegister, addrRegister, 0)));\n        }\n      }\n    }\n    case 3: {\n      QBDI_REQUIRE_ABORT(wback, \"Two tempReg are needed with readSize==3 {}\",\n                         patch);\n      if (isARM) {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(ldrb(tmpRegister, addrRegister, 0)),\n            NoReloc::unique(ldrb(addrRegister, addrRegister, 2)),\n            NoReloc::unique(\n                orrshift(tmpRegister, tmpRegister, addrRegister, 2)));\n      } else {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(t2ldrb(tmpRegister, addrRegister, 0)),\n            NoReloc::unique(t2ldrb(addrRegister, addrRegister, 2)),\n            NoReloc::unique(\n                t2orrshift(tmpRegister, tmpRegister, addrRegister, 2)));\n      }\n    }\n    case 4:\n    case 8:\n    case 12:\n    case 20:\n    case 28:\n    case 36:\n    case 44:\n    case 52:\n    case 60: {\n      if (isARM) {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrPost(tmpRegister, addrRegister, 4)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldri12(tmpRegister, addrRegister, 0)));\n        }\n      } else {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrPost(tmpRegister, addrRegister, 4)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldri12(tmpRegister, addrRegister, 0)));\n        }\n      }\n    }\n    default:\n      QBDI_ABORT(\"Unexpected Read Size {} {}\", readSize, patch);\n  }\n}\n\n// GetWrittenValue\n// ===============\n\nRelocatableInst::UniquePtrVec\nGetWrittenValue::generate(const Patch &patch, TempManager &temp_manager) const {\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    if (index == 0) {\n      return conv_unique<RelocatableInst>(LoadImm::unique(tmpRegister, 0));\n    } else {\n      return {};\n    }\n  }\n\n  Reg addrRegister = temp_manager.getRegForTemp(addr);\n\n  bool isARM = (patch.llvmcpu->getCPUMode() == CPUMode::ARM);\n  bool wback = (tmpRegister != addrRegister);\n\n  unsigned writeSize = getWriteSize(patch.metadata.inst, *patch.llvmcpu);\n  switch (writeSize) {\n    case 6:\n      if (index != 1) {\n        writeSize = 4;\n      } else {\n        writeSize = 2;\n      }\n    default:\n      break;\n  }\n  switch (writeSize) {\n    case 1: {\n      if (isARM) {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrbPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrb(tmpRegister, addrRegister, 0)));\n        }\n      } else {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrbPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrb(tmpRegister, addrRegister, 0)));\n        }\n      }\n    }\n    case 2: {\n      if (isARM) {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrhPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrh(tmpRegister, addrRegister, 0)));\n        }\n      } else {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrhPost(tmpRegister, addrRegister)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrh(tmpRegister, addrRegister, 0)));\n        }\n      }\n    }\n    case 3: {\n      QBDI_REQUIRE_ABORT(wback, \"Two tempReg are needed with readSize==3 {}\",\n                         patch);\n      if (isARM) {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(ldrb(tmpRegister, addrRegister, 0)),\n            NoReloc::unique(ldrb(addrRegister, addrRegister, 2)),\n            NoReloc::unique(\n                orrshift(tmpRegister, tmpRegister, addrRegister, 2)));\n      } else {\n        return conv_unique<RelocatableInst>(\n            NoReloc::unique(t2ldrb(tmpRegister, addrRegister, 0)),\n            NoReloc::unique(t2ldrb(addrRegister, addrRegister, 2)),\n            NoReloc::unique(\n                t2orrshift(tmpRegister, tmpRegister, addrRegister, 2)));\n      }\n    }\n    case 4:\n    case 8:\n    case 12:\n    case 20:\n    case 28:\n    case 36:\n    case 44:\n    case 52:\n    case 60: {\n      if (isARM) {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldrPost(tmpRegister, addrRegister, 4)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(ldri12(tmpRegister, addrRegister, 0)));\n        }\n      } else {\n        if (wback) {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldrPost(tmpRegister, addrRegister, 4)));\n        } else {\n          return conv_unique<RelocatableInst>(\n              NoReloc::unique(t2ldri12(tmpRegister, addrRegister, 0)));\n        }\n      }\n    }\n    default:\n      QBDI_ABORT(\"Unexpected Write Size {}\", writeSize, patch);\n  }\n}\n\n// BackupValueX2\n// =============\n\nRelocatableInst::UniquePtrVec\nBackupValueX2::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg tmpRegister = temp_manager.getRegForTemp(temp);\n  Reg tmp2Register = temp_manager.getRegForTemp(temp2);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    // only set to zero for the first BackupValueX2\n    if (shadow.getTag() != shadow2.getTag()) {\n      return conv_unique<RelocatableInst>(LoadImm::unique(tmpRegister, 0),\n                                          LoadImm::unique(tmp2Register, 0));\n    } else {\n      return {};\n    }\n  }\n\n  Reg addrRegister = temp_manager.getRegForTemp(addr);\n\n  QBDI_REQUIRE_ABORT(tmpRegister != tmp2Register,\n                     \"Need different TempRegister {}\", patch);\n  QBDI_REQUIRE_ABORT(tmpRegister != addrRegister,\n                     \"Need different TempRegister {}\", patch);\n  QBDI_REQUIRE_ABORT(tmp2Register != addrRegister,\n                     \"Need different TempRegister {}\", patch);\n\n  unsigned mask = (1 << tmpRegister.getID()) | (1 << tmp2Register.getID());\n\n  if (patch.llvmcpu->getCPUMode() == CPUMode::ARM) {\n    if (tmpRegister.getID() < tmp2Register.getID()) {\n      return conv_unique<RelocatableInst>(\n          NoReloc::unique(ldmia(addrRegister, mask, true)),\n          StoreShadow::unique(tmpRegister, shadow, true),\n          StoreShadow::unique(tmp2Register, shadow2, true));\n    } else {\n      return conv_unique<RelocatableInst>(\n          NoReloc::unique(ldmia(addrRegister, mask, true)),\n          StoreShadow::unique(tmp2Register, shadow, true),\n          StoreShadow::unique(tmpRegister, shadow2, true));\n    }\n  } else {\n    if (tmpRegister.getID() < tmp2Register.getID()) {\n      return conv_unique<RelocatableInst>(\n          NoReloc::unique(t2ldmia(addrRegister, mask, true)),\n          StoreShadow::unique(tmpRegister, shadow, true),\n          StoreShadow::unique(tmp2Register, shadow2, true));\n    } else {\n      return conv_unique<RelocatableInst>(\n          NoReloc::unique(t2ldmia(addrRegister, mask, true)),\n          StoreShadow::unique(tmp2Register, shadow, true),\n          StoreShadow::unique(tmpRegister, shadow2, true));\n    }\n  }\n}\n\n// CondExclusifLoad\n// ================\n\nRelocatableInst::UniquePtrVec\nCondExclusifLoad::generate(const Patch &patch,\n                           TempManager &temp_manager) const {\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n\n  if (cpumode == CPUMode::ARM) {\n    allocateConsecutiveTempRegister(temp_manager, temp, temp2);\n  }\n\n  Reg tmpReg = temp_manager.getRegForTemp(temp);\n  Reg cpyFlags = temp_manager.getRegForTemp(temp2);\n  bool hasCond = (patch.metadata.archMetadata.cond != llvm::ARMCC::AL);\n\n  // ==== generate the load code ====\n  RelocatableInst::UniquePtrVec loadPatch;\n\n  // default case : restore flags and continue\n  loadPatch.push_back(Msr(cpumode, cpyFlags));\n\n  // case 8 (LDREXD)\n  RelocatableInst::UniquePtrVec tmpPatch8_block;\n\n  tmpPatch8_block.push_back(Msr(cpumode, cpyFlags));\n  tmpPatch8_block.push_back(LoadDataBlock::unique(\n      tmpReg, offsetof(Context, gprState.localMonitor.addr)));\n  if (cpumode == CPUMode::Thumb) {\n    if (hasCond) {\n      tmpPatch8_block.push_back(T2it(cpumode, patch.metadata.archMetadata.cond,\n                                     (unsigned)llvm::ARM::PredBlockMask::T));\n    }\n    tmpPatch8_block.push_back(NoReloc::unique(\n        t2ldrexd(tmpReg, cpyFlags, tmpReg, patch.metadata.archMetadata.cond)));\n  } else {\n    tmpPatch8_block.push_back(NoReloc::unique(\n        ldrexd(tmpReg, cpyFlags, tmpReg, patch.metadata.archMetadata.cond)));\n  }\n  tmpPatch8_block.push_back(\n      Branch(cpumode, getUniquePtrVecSize(loadPatch, *patch.llvmcpu),\n             /* addBranchLen */ true));\n\n  RelocatableInst::UniquePtrVec tmpPatch8_cond;\n\n  tmpPatch8_cond.push_back(Cmp(cpumode, tmpReg, 8));\n  tmpPatch8_cond.push_back(\n      BranchCC(cpumode, getUniquePtrVecSize(tmpPatch8_block, *patch.llvmcpu),\n               llvm::ARMCC::CondCodes::NE,\n               /* withinITBlock */ false, /* addBranchLen */ true));\n\n  prepend(tmpPatch8_block, std::move(tmpPatch8_cond));\n  prepend(loadPatch, std::move(tmpPatch8_block));\n\n  // case 4 (LDREX)\n  RelocatableInst::UniquePtrVec tmpPatch4_block;\n\n  tmpPatch4_block.push_back(Msr(cpumode, cpyFlags));\n  tmpPatch4_block.push_back(LoadDataBlock::unique(\n      tmpReg, offsetof(Context, gprState.localMonitor.addr)));\n  if (cpumode == CPUMode::Thumb) {\n    if (hasCond) {\n      tmpPatch4_block.push_back(T2it(cpumode, patch.metadata.archMetadata.cond,\n                                     (unsigned)llvm::ARM::PredBlockMask::T));\n    }\n    tmpPatch4_block.push_back(NoReloc::unique(\n        t2ldrex(tmpReg, tmpReg, patch.metadata.archMetadata.cond)));\n  } else {\n    tmpPatch4_block.push_back(NoReloc::unique(\n        ldrex(tmpReg, tmpReg, patch.metadata.archMetadata.cond)));\n  }\n  tmpPatch4_block.push_back(\n      Branch(cpumode, getUniquePtrVecSize(loadPatch, *patch.llvmcpu),\n             /* addBranchLen */ true));\n\n  RelocatableInst::UniquePtrVec tmpPatch4_cond;\n\n  tmpPatch4_cond.push_back(Cmp(cpumode, tmpReg, 4));\n  tmpPatch4_cond.push_back(\n      BranchCC(cpumode, getUniquePtrVecSize(tmpPatch4_block, *patch.llvmcpu),\n               llvm::ARMCC::CondCodes::NE,\n               /* withinITBlock */ false, /* addBranchLen */ true));\n\n  prepend(tmpPatch4_block, std::move(tmpPatch4_cond));\n  prepend(loadPatch, std::move(tmpPatch4_block));\n\n  // case 2 (LDREXH)\n  RelocatableInst::UniquePtrVec tmpPatch2_block;\n\n  tmpPatch2_block.push_back(Msr(cpumode, cpyFlags));\n  tmpPatch2_block.push_back(LoadDataBlock::unique(\n      tmpReg, offsetof(Context, gprState.localMonitor.addr)));\n  if (cpumode == CPUMode::Thumb) {\n    if (hasCond) {\n      tmpPatch2_block.push_back(T2it(cpumode, patch.metadata.archMetadata.cond,\n                                     (unsigned)llvm::ARM::PredBlockMask::T));\n    }\n    tmpPatch2_block.push_back(NoReloc::unique(\n        t2ldrexh(tmpReg, tmpReg, patch.metadata.archMetadata.cond)));\n  } else {\n    tmpPatch2_block.push_back(NoReloc::unique(\n        ldrexh(tmpReg, tmpReg, patch.metadata.archMetadata.cond)));\n  }\n  tmpPatch2_block.push_back(\n      Branch(cpumode, getUniquePtrVecSize(loadPatch, *patch.llvmcpu),\n             /* addBranchLen */ true));\n\n  RelocatableInst::UniquePtrVec tmpPatch2_cond;\n\n  tmpPatch2_cond.push_back(Cmp(cpumode, tmpReg, 2));\n  tmpPatch2_cond.push_back(\n      BranchCC(cpumode, getUniquePtrVecSize(tmpPatch2_block, *patch.llvmcpu),\n               llvm::ARMCC::CondCodes::NE,\n               /* withinITBlock */ false, /* addBranchLen */ true));\n\n  prepend(tmpPatch2_block, std::move(tmpPatch2_cond));\n  prepend(loadPatch, std::move(tmpPatch2_block));\n\n  // case 1 (LDREXB)\n  RelocatableInst::UniquePtrVec tmpPatch1_block;\n\n  tmpPatch1_block.push_back(Msr(cpumode, cpyFlags));\n  tmpPatch1_block.push_back(LoadDataBlock::unique(\n      tmpReg, offsetof(Context, gprState.localMonitor.addr)));\n  if (cpumode == CPUMode::Thumb) {\n    if (hasCond) {\n      tmpPatch1_block.push_back(T2it(cpumode, patch.metadata.archMetadata.cond,\n                                     (unsigned)llvm::ARM::PredBlockMask::T));\n    }\n    tmpPatch1_block.push_back(NoReloc::unique(\n        t2ldrexb(tmpReg, tmpReg, patch.metadata.archMetadata.cond)));\n  } else {\n    tmpPatch1_block.push_back(NoReloc::unique(\n        ldrexb(tmpReg, tmpReg, patch.metadata.archMetadata.cond)));\n  }\n  tmpPatch1_block.push_back(\n      Branch(cpumode, getUniquePtrVecSize(loadPatch, *patch.llvmcpu),\n             /* addBranchLen */ true));\n\n  RelocatableInst::UniquePtrVec finalPatch;\n\n  finalPatch.push_back(Mrs(cpumode, cpyFlags));\n  finalPatch.push_back(LoadDataBlock::unique(\n      tmpReg, offsetof(Context, gprState.localMonitor.enable)));\n  finalPatch.push_back(Cmp(cpumode, tmpReg, 1));\n  finalPatch.push_back(\n      BranchCC(cpumode, getUniquePtrVecSize(tmpPatch1_block, *patch.llvmcpu),\n               llvm::ARMCC::CondCodes::NE,\n               /* withinITBlock */ false, /* addBranchLen */ true));\n\n  append(finalPatch, std::move(tmpPatch1_block));\n  append(finalPatch, std::move(loadPatch));\n  return finalPatch;\n}\n\n// SetCondReachAndJump\n// ===================\n\nPatchGenerator::UniquePtr SetCondReachAndJump::clone() const {\n  PatchGenerator::UniquePtrVec newVec;\n\n  for (const PatchGenerator::UniquePtr &g : patchVec) {\n    newVec.push_back(g->clone());\n  }\n\n  return SetCondReachAndJump::unique(temp, tag, std::move(newVec));\n}\n\nRelocatableInst::UniquePtrVec\nSetCondReachAndJump::generate(const Patch &patch,\n                              TempManager &temp_manager) const {\n\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL) {\n\n    RelocatableInst::UniquePtrVec instru;\n\n    if (tag.getTag() != ShadowReservedTag::Untagged) {\n      Reg tmpRegister = temp_manager.getRegForTemp(temp);\n\n      instru.push_back(LoadImm::unique(tmpRegister, 0));\n      if (cpumode == CPUMode::Thumb) {\n        instru.push_back(T2it(cpumode, patch.metadata.archMetadata.cond,\n                              (unsigned)llvm::ARM::PredBlockMask::T));\n      }\n      instru.push_back(\n          LoadImmCC::unique(tmpRegister, 1, patch.metadata.archMetadata.cond));\n      instru.push_back(StoreShadow::unique(tmpRegister, tag, true));\n    }\n\n    RelocatableInst::UniquePtrVec tmpInstru;\n    for (const PatchGenerator::UniquePtr &g : patchVec) {\n      append(tmpInstru, g->generate(patch, temp_manager));\n    }\n\n    rword patchSize = getUniquePtrVecSize(tmpInstru, *patch.llvmcpu);\n    if (patchSize > 0) {\n      instru.push_back(BranchCC(\n          patch.llvmcpu->getCPUMode(), patchSize,\n          llvm::ARMCC::getOppositeCondition(\n              (llvm::ARMCC::CondCodes)patch.metadata.archMetadata.cond),\n          /* withinITBlock */ false, /* addBranchLen */ true));\n\n      append(instru, std::move(tmpInstru));\n    }\n    return instru;\n  } else {\n    RelocatableInst::UniquePtrVec instru;\n    for (const PatchGenerator::UniquePtr &g : patchVec) {\n      append(instru, g->generate(patch, temp_manager));\n    }\n    return instru;\n  }\n}\n\n// ItPatch\n// =======\n\nRelocatableInst::UniquePtrVec\nItPatch::generate(const Patch &patch, TempManager &temp_manager) const {\n  CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb,\n                     \"Only available in Thumb mode {}\", patch);\n\n  // we return a null patch if the instruction isn't in a ITblock\n  // If the instruction is in a ITblock with the condition AL, we must preserve\n  // the block to keep the flags behavior\n  if (patch.metadata.archMetadata.posITblock == 0) {\n    for (unsigned i = 0; i < nbcond; i++) {\n      QBDI_REQUIRE_ABORT(cond[i] == false,\n                         \"Use ItPatch with invCond ({}) on a instruction \"\n                         \"outside of ITBlock {}\",\n                         i, patch);\n    }\n    return RelocatableInst::UniquePtrVec();\n  }\n\n  llvm::ARM::PredBlockMask mask = llvm::ARM::PredBlockMask::T;\n\n  if (patch.metadata.archMetadata.cond == llvm::ARMCC::AL) {\n    for (unsigned i = 0; i < nbcond; i++) {\n      QBDI_REQUIRE_ABORT(cond[i] == false,\n                         \"Use ItPatch with invCond ({}) on AL cond {}\", i,\n                         patch);\n    }\n  }\n\n  for (unsigned i = 1; i < nbcond; i++) {\n    if (cond[0] ^ cond[i]) {\n      mask = llvm::expandPredBlockMask(mask, llvm::ARMVCC::VPTCodes::Else);\n    } else {\n      mask = llvm::expandPredBlockMask(mask, llvm::ARMVCC::VPTCodes::Then);\n    }\n  }\n  if (cond[0]) {\n    return conv_unique<RelocatableInst>(\n        T2it(cpumode,\n             llvm::ARMCC::getOppositeCondition(\n                 (llvm::ARMCC::CondCodes)patch.metadata.archMetadata.cond),\n             (unsigned)mask));\n  } else {\n    return conv_unique<RelocatableInst>(\n        T2it(cpumode, patch.metadata.archMetadata.cond, (unsigned)mask));\n  }\n}\n\n// TPopPatchGen\n// ============\n\nRelocatableInst::UniquePtrVec\nTPopPatchGen::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb,\n                     \"Only available in Thumb mode {}\", patch);\n\n  const unsigned numOperands = inst.getNumOperands();\n\n  // verify if the instruction need PC\n  QBDI_REQUIRE_ABORT(0 < numOperands, \"Invalid instruction {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(numOperands - 1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(numOperands - 1).getReg() ==\n                         GPR_ID[REG_PC],\n                     \"Unexpected PC behavior {}\", patch);\n\n  // number of register to pop (included pc)\n  unsigned listRegsNum = numOperands - 2;\n\n  // get tempReg\n  Reg tempReg = temp_manager.getRegForTemp(temp);\n\n  // begin the patch\n  RelocatableInst::UniquePtrVec relocInstList;\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL) {\n    const unsigned invCond = llvm::ARMCC::getOppositeCondition(\n        (llvm::ARMCC::CondCodes)patch.metadata.archMetadata.cond);\n    if (listRegsNum > 1) {\n      relocInstList.push_back(\n          T2it(cpumode, invCond, (unsigned)llvm::ARM::PredBlockMask::TEE));\n    } else {\n      relocInstList.push_back(\n          T2it(cpumode, invCond, (unsigned)llvm::ARM::PredBlockMask::TE));\n    }\n    relocInstList.push_back(LoadImmCC::unique(\n        tempReg, Constant(patch.metadata.endAddress() | 1), invCond));\n  }\n  if (listRegsNum > 1) {\n    // generate the pop instruction without PC\n    llvm::MCInst instNoPC = inst;\n    instNoPC.erase(instNoPC.begin() + (numOperands - 1));\n\n    relocInstList.push_back(NoReloc::unique(std::move(instNoPC)));\n  }\n\n  relocInstList.push_back(NoReloc::unique(\n      t2ldrPost(tempReg, GPR_ID[REG_SP], 4, patch.metadata.archMetadata.cond)));\n\n  append(relocInstList, WritePC(temp).generate(patch, temp_manager));\n\n  return relocInstList;\n}\n\n// T2LDMPatchGen\n// =============\n\nstatic void gent2LDMsubPatch(RelocatableInst::UniquePtrVec &vec,\n                             const Patch &patch, Reg tempReg, RegLLVM addrReg,\n                             unsigned startOp, unsigned endOp, bool pendingTmp,\n                             bool IA) {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  if (endOp <= startOp) {\n    return;\n  } else if (startOp + 1 == endOp) {\n    QBDI_REQUIRE_ABORT(startOp < inst.getNumOperands(), \"Invalid operand {}\",\n                       patch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(startOp).isReg(),\n                       \"Unexpected operand type {}\", patch);\n    RegLLVM destReg = inst.getOperand(startOp).getReg();\n    if (IA) {\n      vec.push_back(NoReloc::unique(t2ldrPost(destReg, addrReg, 4)));\n    } else {\n      vec.push_back(NoReloc::unique(t2ldrPre(destReg, addrReg, -4)));\n    }\n    if (pendingTmp) {\n      QBDI_REQUIRE_ABORT(\n          destReg == tempReg,\n          \"The loaded register is expected to be the TempRegister {}\", patch);\n      vec.push_back(StoreDataBlock::unique(tempReg, Offset(tempReg)));\n    } else {\n      QBDI_REQUIRE_ABORT(\n          destReg != tempReg,\n          \"The loaded register isn't expected to be the TempRegister {}\",\n          patch);\n    }\n  } else {\n    unsigned mask = 0;\n    bool foundTmpReg = false;\n    QBDI_REQUIRE_ABORT(endOp <= inst.getNumOperands(), \"Invalid operand {}\",\n                       patch);\n    for (unsigned i = startOp; i < endOp; i++) {\n      QBDI_REQUIRE_ABORT(inst.getOperand(i).isReg(),\n                         \"Unexpected operand {} type {}\", i, patch);\n      RegLLVM r = inst.getOperand(i).getReg();\n      switch (r.getValue()) {\n        case llvm::ARM::R0:\n        case llvm::ARM::R1:\n        case llvm::ARM::R2:\n        case llvm::ARM::R3:\n        case llvm::ARM::R4:\n        case llvm::ARM::R5:\n        case llvm::ARM::R6:\n        case llvm::ARM::R7:\n        case llvm::ARM::R8:\n        case llvm::ARM::R9:\n        case llvm::ARM::R10:\n        case llvm::ARM::R11:\n        case llvm::ARM::R12:\n          mask |= (1 << (r.getValue() - llvm::ARM::R0));\n          break;\n        case llvm::ARM::LR:\n          mask |= (1 << 14);\n          break;\n        // SP cannot be set in Thumb\n        case llvm::ARM::SP:\n        // PC is handle by the caller and should be in the list\n        case llvm::ARM::PC:\n        default:\n          QBDI_ABORT(\"Unexpected register {} {}\", r.getValue(), patch);\n      }\n      if (r == tempReg) {\n        QBDI_REQUIRE_ABORT(not foundTmpReg, \"TempReg already found {}\", patch);\n        QBDI_REQUIRE_ABORT(pendingTmp,\n                           \"Unexpected TempReg in the register list {}\", patch);\n        foundTmpReg = true;\n      }\n    }\n    if (IA) {\n      vec.push_back(NoReloc::unique(t2ldmia(addrReg, mask, true)));\n    } else {\n      vec.push_back(NoReloc::unique(t2ldmdb(addrReg, mask, true)));\n    }\n    if (pendingTmp) {\n      QBDI_REQUIRE_ABORT(foundTmpReg,\n                         \"TempReg not found in the register list {}\", patch);\n      vec.push_back(StoreDataBlock::unique(tempReg, Offset(tempReg)));\n    }\n  }\n}\n\nRelocatableInst::UniquePtrVec\nT2LDMPatchGen::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb,\n                     \"Only available in Thumb mode {}\", patch);\n\n  const unsigned opcode = inst.getOpcode();\n  const unsigned numOperands = inst.getNumOperands();\n\n  // verify if the instruction need PC\n  QBDI_REQUIRE_ABORT(0 < numOperands, \"Invalid instruction {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(numOperands - 1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  bool needPC = (inst.getOperand(numOperands - 1).getReg() == GPR_ID[REG_PC]);\n  QBDI_REQUIRE_ABORT(writePC == needPC, \"Unexpected PC behavior {}\", patch);\n\n  // get baseAddr\n  QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  RegLLVM addrReg = inst.getOperand(0).getReg();\n\n  // get the number of register in the register list\n  unsigned listRegsNum;\n  bool wback;\n\n  switch (opcode) {\n    case llvm::ARM::t2LDMIA:\n    case llvm::ARM::t2LDMDB:\n      listRegsNum = numOperands - 3;\n      wback = false;\n      break;\n    case llvm::ARM::t2LDMIA_UPD:\n    case llvm::ARM::t2LDMDB_UPD:\n      listRegsNum = numOperands - 4;\n      wback = true;\n      break;\n    default:\n      QBDI_ABORT(\"Unexpected instruction {}\", patch);\n  }\n\n  // verify if the flag RegisterUsage::RegisterSavedScratch is set\n  Reg reservedSavedScratch = 0;\n  bool unusedSavedScratch = true;\n  bool foundSavedScratch = false;\n\n  for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n    if (patch.regUsage[i] != 0 and\n        (patch.regUsage[i] & RegisterUsage::RegisterSavedScratch) != 0) {\n      QBDI_REQUIRE_ABORT(not foundSavedScratch,\n                         \"Maximum one reservedSavedScratch {}\", patch);\n      QBDI_REQUIRE_ABORT(Reg(i) != addrReg,\n                         \"baseReg must not be the scratchRegister {}\", patch);\n      unusedSavedScratch =\n          ((patch.regUsage[i] & RegisterUsage::RegisterBoth) == 0);\n      reservedSavedScratch = Reg(i);\n      foundSavedScratch = true;\n    }\n  }\n\n  // for t2LDM, we can used the original instruction if:\n  // - PC isn't in the register list AND\n  // - reservedSavedScratch isn't set or used by the instruction\n  if (unusedSavedScratch and not needPC) {\n    RelocatableInst::UniquePtrVec relocInstList;\n    append(relocInstList, ItPatch(false).generate(patch, temp_manager));\n    append(relocInstList, ModifyInstruction(InstTransform::UniquePtrVec())\n                              .generate(patch, temp_manager));\n    // force the temp_manager to not allocate tempRegister in this case\n    // Otherwith, we are not sure that the tempRegister isn't in the list of\n    // restored registers.\n    temp_manager.lockTempManager();\n    QBDI_REQUIRE_ABORT(temp_manager.getUsedRegisters().size() == 0,\n                       \"Unexpected TempManager state {}\", patch);\n    return relocInstList;\n  }\n\n  // lock tempRegister. Only 1 register may be a temp register\n  Reg tempReg = temp_manager.getRegForTemp(temp);\n  temp_manager.lockTempManager();\n  QBDI_REQUIRE_ABORT(temp_manager.getUsedRegisters().size() == 1,\n                     \"Unexpected TempManager state {}\", patch);\n  QBDI_REQUIRE_ABORT(tempReg != addrReg, \"tempRegister allocation error {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(tempReg != reservedSavedScratch or not foundSavedScratch,\n                     \"tempRegister allocation error {}\", patch);\n\n  RelocatableInst::UniquePtrVec relocInstList;\n\n  switch (opcode) {\n    case llvm::ARM::t2LDMIA:\n    case llvm::ARM::t2LDMIA_UPD: {\n      // create a range of register to add\n      unsigned firstPendingReg = numOperands - listRegsNum;\n      // does the tmp register is in the range of loaded register\n      bool pendingTmp = false;\n      // manage the base register\n      // If the base register is in the list, keep his position to load it at\n      // the end\n      bool addrRegInList = false;\n      unsigned offsetAddrReg = 0;\n      for (unsigned i = firstPendingReg; i < numOperands; i++) {\n        QBDI_REQUIRE_ABORT(inst.getOperand(i).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM r = inst.getOperand(i).getReg();\n        if (r == tempReg) {\n          pendingTmp = true;\n        } else if (r == reservedSavedScratch and foundSavedScratch) {\n          gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg,\n                           firstPendingReg, i, pendingTmp, true);\n          relocInstList.push_back(\n              NoReloc::unique(t2ldrPost(tempReg, addrReg, 4)));\n          relocInstList.push_back(MovToSavedScratchReg::unique(\n              tempReg, reservedSavedScratch, llvm::ARMCC::AL));\n          pendingTmp = false;\n          firstPendingReg = i + 1;\n        } else if (r == addrReg) {\n          QBDI_REQUIRE_ABORT(\n              not wback,\n              \"Writeback when the address register is loaded is Undefined {}\",\n              patch);\n          gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg,\n                           firstPendingReg, i, pendingTmp, true);\n          // skip the value for now\n          append(relocInstList, Addc(cpumode, addrReg, addrReg, 4, tempReg));\n          pendingTmp = false;\n          firstPendingReg = i + 1;\n          addrRegInList = true;\n          offsetAddrReg = i - (numOperands - listRegsNum);\n        } else if (r == Reg(REG_PC)) {\n          gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg,\n                           firstPendingReg, i, pendingTmp, true);\n          relocInstList.push_back(\n              NoReloc::unique(t2ldrPost(tempReg, addrReg, 4)));\n          append(\n              relocInstList,\n              WritePC(temp, /* dropCond */ true).generate(patch, temp_manager));\n          pendingTmp = false;\n          firstPendingReg = i + 1;\n        }\n      }\n      gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg, firstPendingReg,\n                       numOperands, pendingTmp, true);\n      if (addrRegInList) {\n        relocInstList.push_back(NoReloc::unique(\n            t2ldri8(addrReg, addrReg, -4 * (listRegsNum - offsetAddrReg))));\n      } else if (not wback) {\n        append(relocInstList,\n               Addc(cpumode, addrReg, addrReg, -4 * listRegsNum, tempReg));\n      }\n      break;\n    }\n    case llvm::ARM::t2LDMDB:\n    case llvm::ARM::t2LDMDB_UPD: {\n      unsigned lastPendingReg = numOperands;\n      bool pendingTmp = false;\n      bool addrRegInList = false;\n      unsigned offsetAddrReg = 0;\n      for (unsigned i = numOperands - 1; i >= (numOperands - listRegsNum);\n           i--) {\n        QBDI_REQUIRE_ABORT(inst.getOperand(i).isReg(),\n                           \"Unexpected operand type {}\", patch);\n        RegLLVM r = inst.getOperand(i).getReg();\n        if (r == tempReg) {\n          pendingTmp = true;\n        } else if (r == reservedSavedScratch and foundSavedScratch) {\n          gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg, i + 1,\n                           lastPendingReg, pendingTmp, false);\n          relocInstList.push_back(\n              NoReloc::unique(t2ldrPre(tempReg, addrReg, -4)));\n          relocInstList.push_back(MovToSavedScratchReg::unique(\n              tempReg, reservedSavedScratch, llvm::ARMCC::AL));\n          pendingTmp = false;\n          lastPendingReg = i;\n        } else if (r == addrReg) {\n          QBDI_REQUIRE_ABORT(\n              not wback,\n              \"Writeback when the address register is loaded is Undefined {}\",\n              patch);\n          gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg, i + 1,\n                           lastPendingReg, pendingTmp, false);\n          // skip the value for now\n          append(relocInstList, Addc(cpumode, addrReg, addrReg, -4, tempReg));\n          pendingTmp = false;\n          lastPendingReg = i;\n          addrRegInList = true;\n          offsetAddrReg = i - (numOperands - listRegsNum);\n        } else if (r == Reg(REG_PC)) {\n          gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg, i + 1,\n                           lastPendingReg, pendingTmp, false);\n          relocInstList.push_back(\n              NoReloc::unique(t2ldrPre(tempReg, addrReg, -4)));\n          append(\n              relocInstList,\n              WritePC(temp, /* dropCond */ true).generate(patch, temp_manager));\n          pendingTmp = false;\n          lastPendingReg = i;\n        }\n      }\n      gent2LDMsubPatch(relocInstList, patch, tempReg, addrReg,\n                       numOperands - listRegsNum, lastPendingReg, pendingTmp,\n                       false);\n      if (addrRegInList) {\n        relocInstList.push_back(\n            NoReloc::unique(t2ldri12(addrReg, addrReg, 4 * offsetAddrReg)));\n      } else if (not wback) {\n        append(relocInstList,\n               Addc(cpumode, addrReg, addrReg, 4 * listRegsNum, tempReg));\n      }\n      break;\n    }\n    default:\n      QBDI_ABORT(\"Unexpected instruction {}\", patch);\n  }\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL) {\n    const unsigned invCond = llvm::ARMCC::getOppositeCondition(\n        (llvm::ARMCC::CondCodes)patch.metadata.archMetadata.cond);\n    rword patchSize = getUniquePtrVecSize(relocInstList, *patch.llvmcpu);\n\n    if (needPC) {\n      // if need pc, create a it block to set PC and jump over if the condition\n      // isn't reach\n      RelocatableInst::UniquePtrVec relocInstListPre;\n\n      relocInstListPre.push_back(\n          T2it(cpumode, invCond, (unsigned)llvm::ARM::PredBlockMask::TTT));\n      relocInstListPre.push_back(LoadImmCC::unique(\n          tempReg, Constant(patch.metadata.endAddress() | 1), invCond));\n      // no WritePC here, because we generate the case where the condition isn't\n      // reach. We also need to have a fine control of the number of instruction\n      // for the current ITblock\n      relocInstListPre.push_back(\n          StoreDataBlockCC::unique(tempReg, Offset(Reg(REG_PC)), invCond));\n      relocInstListPre.push_back(BranchCC(cpumode, patchSize, invCond,\n                                          /* withinITBlock */ true,\n                                          /* addBranchLen */ true));\n\n      prepend(relocInstList, std::move(relocInstListPre));\n    } else {\n      // if no need of PC, just jump over the whole patch\n      relocInstList.insert(relocInstList.begin(),\n                           BranchCC(cpumode, patchSize, invCond,\n                                    /* withinITBlock */ false,\n                                    /* addBranchLen */ true));\n    }\n  }\n  return relocInstList;\n}\n\n// T2STMPatchGen\n// =============\n\nRelocatableInst::UniquePtrVec\nT2STMPatchGen::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb,\n                     \"Only available in Thumb mode {}\", patch);\n\n  const unsigned opcode = inst.getOpcode();\n  const unsigned numOperands = inst.getNumOperands();\n\n  // verify if the instruction need PC (PC not supported)\n  QBDI_REQUIRE_ABORT(0 < numOperands, \"Invalid instruction {}\", patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(numOperands - 1).isReg(),\n                     \"Unexpected operand type {}\", patch);\n  bool needPC = (inst.getOperand(numOperands - 1).getReg() == GPR_ID[REG_PC]);\n  QBDI_REQUIRE_ABORT(not needPC, \"T2STM with PC is undefined {}\", patch);\n\n  // get baseAddr\n  QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  RegLLVM addrReg = inst.getOperand(0).getReg();\n\n  // get the number of register in the register list\n  unsigned listRegsNum;\n\n  switch (opcode) {\n    case llvm::ARM::t2STMIA:\n    case llvm::ARM::t2STMDB:\n      listRegsNum = numOperands - 3;\n      break;\n    case llvm::ARM::t2STMIA_UPD:\n    case llvm::ARM::t2STMDB_UPD:\n      listRegsNum = numOperands - 4;\n      break;\n    default:\n      QBDI_ABORT(\"Unexpected instruction {}\", patch);\n  }\n\n  // verify if the flag RegisterUsage::RegisterSavedScratch is set\n  Reg reservedSavedScratch = 0;\n  bool unusedSavedScratch = true;\n  bool foundSavedScratch = false;\n\n  for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n    if (patch.regUsage[i] != 0 and\n        (patch.regUsage[i] & RegisterUsage::RegisterSavedScratch) != 0) {\n      QBDI_REQUIRE_ABORT(not foundSavedScratch,\n                         \"Maximum one reservedSavedScratch {}\", patch);\n      QBDI_REQUIRE_ABORT(Reg(i) != addrReg,\n                         \"baseReg must not be the scratchRegister {}\", patch);\n      unusedSavedScratch =\n          ((patch.regUsage[i] & RegisterUsage::RegisterBoth) == 0);\n      reservedSavedScratch = Reg(i);\n      foundSavedScratch = true;\n    }\n  }\n\n  // for t2STM, we can used the original instruction if:\n  // - reservedSavedScratch isn't set or used by the instruction\n  if (unusedSavedScratch) {\n    RelocatableInst::UniquePtrVec relocInstList;\n    append(relocInstList, ItPatch(false).generate(patch, temp_manager));\n    append(relocInstList, ModifyInstruction(InstTransform::UniquePtrVec())\n                              .generate(patch, temp_manager));\n    // force the temp_manager to not allocate tempRegister in this case\n    // Otherwith, we are not sure that the tempRegister isn't in the list of\n    // restored registers.\n    temp_manager.lockTempManager();\n    QBDI_REQUIRE_ABORT(temp_manager.getUsedRegisters().size() == 0,\n                       \"Unexpected TempManager state {}\", patch);\n    return relocInstList;\n  }\n\n  // lock tempRegister. Only 1 register may be a temp register\n  Reg tempReg = temp_manager.getRegForTemp(temp);\n  temp_manager.lockTempManager();\n  QBDI_REQUIRE_ABORT(temp_manager.getUsedRegisters().size() == 1,\n                     \"Unexpected TempManager state {}\", patch);\n  QBDI_REQUIRE_ABORT(tempReg != addrReg, \"tempRegister allocation error {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(tempReg != reservedSavedScratch,\n                     \"tempRegister allocation error {}\", patch);\n\n  // verify if the tempReg is a register on the register list\n  // This must be the case, otherwise, the scratch register should be reserved\n  // on a free register\n  bool tempIsNeeded =\n      (patch.regUsage[tempReg.getID()] & RegisterUsage::RegisterBoth) != 0;\n  QBDI_REQUIRE_ABORT(tempIsNeeded, \"Unexpected TempRegister state {}\", patch);\n\n  // search position of scratch register in the list\n  bool foundSavedScratchPosition = false;\n  unsigned savedScratchPosition = 0;\n\n  for (unsigned int i = (numOperands - listRegsNum); i < numOperands; i++) {\n    QBDI_REQUIRE_ABORT(inst.getOperand(i).isReg(), \"Unexpected operand type {}\",\n                       patch);\n    RegLLVM r = inst.getOperand(i).getReg();\n    if (r == reservedSavedScratch) {\n      foundSavedScratchPosition = true;\n      savedScratchPosition = i - (numOperands - listRegsNum);\n      break;\n    }\n  }\n  QBDI_REQUIRE_ABORT(foundSavedScratchPosition,\n                     \"Unexpected usage of the Scratch Register {}\", patch);\n\n  RelocatableInst::UniquePtrVec relocInstList;\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL) {\n    append(relocInstList,\n           ItPatch(false, false, false, false).generate(patch, temp_manager));\n  }\n\n  // load the value of the tempRegister from the Datablock\n  relocInstList.push_back(LoadDataBlockCC::unique(\n      tempReg, Offset(tempReg), patch.metadata.archMetadata.cond));\n\n  // apply the instruction (the value of the scratch register will be fixed\n  // after) appling the same instruction allow us to have the same behaviour if\n  // the base register is stored.\n  append(relocInstList, ModifyInstruction(InstTransform::UniquePtrVec())\n                            .generate(patch, temp_manager));\n\n  // Get ScratchRegister\n  relocInstList.push_back(MovFromSavedScratchReg::unique(\n      reservedSavedScratch, tempReg, patch.metadata.archMetadata.cond));\n\n  unsigned fixOffset;\n\n  switch (inst.getOpcode()) {\n    case llvm::ARM::t2STMIA:\n    case llvm::ARM::t2STMDB_UPD:\n      fixOffset = 4 * savedScratchPosition;\n      break;\n    case llvm::ARM::t2STMDB:\n    case llvm::ARM::t2STMIA_UPD:\n      fixOffset = -4 * (listRegsNum - savedScratchPosition);\n      break;\n    default:\n      QBDI_ABORT(\"STMPatchGen should not be used for this instruction: {}\",\n                 patch);\n  }\n\n  // store ScratchRegister\n  relocInstList.push_back(NoReloc::unique(\n      t2stri8(tempReg, addrReg, fixOffset, patch.metadata.archMetadata.cond)));\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL) {\n    QBDI_REQUIRE_ABORT(relocInstList.size() == 5, \"Unexpected patch size {} {}\",\n                       relocInstList.size(), patch);\n  }\n\n  return relocInstList;\n}\n\n// T2TBBTBHPatchGen\n// ================\n\nRelocatableInst::UniquePtrVec\nT2TBBTBHPatchGen::generate(const Patch &patch,\n                           TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb,\n                     \"Only available in Thumb mode {}\", patch);\n\n  const unsigned opcode = inst.getOpcode();\n\n  // get instruction operand\n  QBDI_REQUIRE_ABORT(2 <= inst.getNumOperands(), \"Invalid instruction {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(0).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  RegLLVM baseReg = inst.getOperand(0).getReg();\n  RegLLVM indexReg = inst.getOperand(1).getReg();\n\n  QBDI_REQUIRE_ABORT(baseReg != GPR_ID[REG_SP],\n                     \"Unsupported SP in baseRegister {}\", patch);\n  QBDI_REQUIRE_ABORT(indexReg != GPR_ID[REG_SP],\n                     \"Unsupported SP in indexRegister {}\", patch);\n  QBDI_REQUIRE_ABORT(indexReg != GPR_ID[REG_PC],\n                     \"Unexpected PC in indexRegister {}\", patch);\n\n  // get our tempRegister\n  Reg tempReg1 = temp_manager.getRegForTemp(temp1);\n  Reg tempReg2 = temp_manager.getRegForTemp(temp2);\n\n  // begin patch\n  RelocatableInst::UniquePtrVec relocInstList;\n\n  // 1. get base address in tempRegister (if baseReg == PC, get PC value)\n  if (baseReg == GPR_ID[REG_PC]) {\n    append(relocInstList, GetPCOffset(temp1, Constant(0), /* keepCond */ false)\n                              .generate(patch, temp_manager));\n    baseReg = tempReg1;\n  }\n\n  // 2. get next PC address\n  // note: TBB and TBH use BranchWritePC => the next instruction mode is always\n  // Thumb\n  //       The value returns by GetNextInstAddr is always odd.\n  append(relocInstList, GetNextInstAddr(temp2, /* keepCond */ false)\n                            .generate(patch, temp_manager));\n\n  // 3. load offset in tempReg1 and add the offset with the next address\n  append(relocInstList, ItPatch(false, false).generate(patch, temp_manager));\n  switch (opcode) {\n    case llvm::ARM::t2TBB:\n      relocInstList.push_back(NoReloc::unique(t2ldrbr(\n          tempReg1, baseReg, indexReg, patch.metadata.archMetadata.cond)));\n      break;\n    case llvm::ARM::t2TBH:\n      relocInstList.push_back(NoReloc::unique(t2ldrhrs(\n          tempReg1, baseReg, indexReg, 1, patch.metadata.archMetadata.cond)));\n      break;\n    default:\n      QBDI_ABORT(\"Unexpected instruction {}\", patch);\n  }\n  relocInstList.push_back(NoReloc::unique(\n      t2addrsi(tempReg2, tempReg2, tempReg1, 1, llvm::ARM_AM::lsl,\n               patch.metadata.archMetadata.cond)));\n\n  // 4. save next PC\n  append(relocInstList, WritePC(temp2).generate(patch, temp_manager));\n\n  return relocInstList;\n}\n\n// T2BXAUTPatchGen\n// ===============\n\nRelocatableInst::UniquePtrVec\nT2BXAUTPatchGen::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const CPUMode cpumode = patch.llvmcpu->getCPUMode();\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb,\n                     \"Only available in Thumb mode {}\", patch);\n\n  // get instruction operand\n  QBDI_REQUIRE_ABORT(5 <= inst.getNumOperands(), \"Invalid instruction {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(2).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(3).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  QBDI_REQUIRE_ABORT(inst.getOperand(4).isReg(), \"Unexpected operand type {}\",\n                     patch);\n  RegLLVM targetReg = inst.getOperand(2).getReg();\n  RegLLVM contextReg = inst.getOperand(3).getReg();\n  RegLLVM encryptedReg = inst.getOperand(4).getReg();\n\n  // begin patch\n  RelocatableInst::UniquePtrVec relocInstList;\n\n  if (patch.metadata.archMetadata.cond != llvm::ARMCC::AL) {\n    append(relocInstList, ItPatch(false).generate(patch, temp_manager));\n  }\n  relocInstList.push_back(NoReloc::unique(t2autg(\n      targetReg, contextReg, encryptedReg, patch.metadata.archMetadata.cond)));\n\n  return relocInstList;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/PatchGenerator_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHGENERATOR_ARM_H\n#define PATCHGENERATOR_ARM_H\n\n#include <array>\n#include <memory>\n#include <stddef.h>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nclass SetDataBlockAddress\n    : public PureEval<AutoClone<PatchGenerator, SetDataBlockAddress>> {\n  Reg reg;\n  bool setScratchRegister;\n\npublic:\n  /* Set reg to the address of the Datablock\n   */\n  SetDataBlockAddress(Reg reg) : reg(reg), setScratchRegister(false) {}\n\n  /* Set the stratch register to the address of the Datablock\n   */\n  SetDataBlockAddress() : reg(0), setScratchRegister(true) {}\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n};\n\nclass WritePC : public AutoClone<PatchGenerator, WritePC> {\n  Temp temp;\n  bool dropCond;\n\npublic:\n  /*! Write PC in the DataBlock. Manage to add or remove the LSB when the\n   *  depending of the instruction behavior and the CPU behavior selected.\n   *\n   * @param[in] temp   The register that containt the new value of PC.\n   */\n  WritePC(Temp temp, bool dropCond = false) : temp(temp), dropCond(dropCond) {}\n\n  /*! Output:\n   *\n   * LDR REG32 temp, MEM32 Shadow(IMM32 1)\n   * STR REG32 temp, MEM32 DSataBlock[Offset(hostState.exchange)]\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override { return true; }\n};\n\nclass SetExchange : public AutoClone<PatchGenerator, SetExchange> {\n  Temp temp;\n\npublic:\n  /*!\n   *\n   * @param[in] temp   Any unused temporary, overwritten by this generator.\n   */\n  SetExchange(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * LDR REG32 temp, MEM32 Shadow(IMM32 1)\n   * STR REG32 temp, MEM32 DSataBlock[Offset(hostState.exchange)]\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetPCOffset : public AutoClone<PatchGenerator, GetPCOffset> {\n\n  Temp temp;\n  Operand opDest;\n  Constant cst;\n  Operand op;\n  enum {\n    TmpConstantType,\n    TmpOperandType,\n    OpOperandType,\n  } type;\n\n  bool keepCond;\n\npublic:\n  /*! Interpret a constant as a RIP relative offset and copy it in a temporary.\n   * It can be used to obtain the current value of RIP by using a constant of 0.\n   *\n   * @param[in] temp     A temporary where the value will be copied.\n   * @param[in] cst      The constant to be used.\n   * @param[in] keepCond keep the condition of the instruction. If false, use AL\n   */\n  GetPCOffset(Temp temp, Constant cst, bool keepCond = false)\n      : temp(temp), opDest(0), cst(cst), op(0), type(TmpConstantType),\n        keepCond(keepCond) {}\n\n  /*! Interpret an operand as a RIP relative offset and copy it in a temporary.\n   * It can be used to obtain jump/call targets or relative memory access\n   * addresses.\n   *\n   * @param[in] temp     A temporary where the value will be copied.\n   * @param[in] op       The  operand index (relative to the instruction\n   *                     LLVM MCInst representation) to be used.\n   * @param[in] keepCond keep the condition of the instruction. If false, use AL\n   */\n  GetPCOffset(Temp temp, Operand op, bool keepCond = false)\n      : temp(temp), opDest(0), cst(0), op(op), type(TmpOperandType),\n        keepCond(keepCond) {}\n\n  /*! Interpret an operand as a RIP relative offset and copy it in an operand.\n   * It can be used to obtain jump/call targets or relative memory access\n   * addresses.\n   *\n   * @param[in] opDest   An operand where the value will be copied.\n   * @param[in] op       The operand index (relative to the instruction\n   *                     LLVM MCInst representation) to be used.\n   * @param[in] keepCond keep the condition of the instruction. If false, use AL\n   */\n  GetPCOffset(Operand opDest, Operand op, bool keepCond = false)\n      : temp(0), opDest(opDest), cst(0), op(op), type(OpOperandType),\n        keepCond(keepCond) {}\n\n  /*! Output:\n   *\n   * If cst:\n   * MOV REG64 temp, IMM64 (cst + address)\n\n   * If op is an immediate:\n   * MOV REG64 temp, IMM64 (op + address)\n   *\n  */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetNextInstAddr : public AutoClone<PatchGenerator, GetNextInstAddr> {\n  Temp temp;\n  Reg reg;\n  bool keepCond;\n  bool invCond;\n  enum {\n    TmpType,\n    RegType,\n  } type;\n\npublic:\n  /*! Get the address of the next instruction\n   * It can be used to obtain the current value of RIP by using a constant of 0.\n   *\n   * @param[in] temp            A temporary where the value will be copied.\n   * @param[in] keepCond        keep the condition of the instruction. If false,\n   *                            use AL\n   * @param[in] invCond         inverse the condition of the instruction.\n   *                            an ItPatch\n   */\n  GetNextInstAddr(Temp temp, bool keepCond = false, bool invCond = false)\n      : temp(temp), reg(0), keepCond(keepCond), invCond(invCond),\n        type(TmpType) {}\n\n  /*! Get the address of the next instruction\n   * It can be used to obtain the current value of RIP by using a constant of 0.\n   *\n   * @param[in] reg             The register where the value will be copied.\n   * @param[in] keepCond        keep the condition of the instruction. If false,\n   *                            use AL\n   * @param[in] invCond         inverse the condition of the instruction.\n   *                            an ItPatch\n   */\n  GetNextInstAddr(Reg reg, bool keepCond = false, bool invCond = false)\n      : temp(0), reg(reg), keepCond(keepCond), invCond(invCond), type(RegType) {\n  }\n\n  /*! Output:\n   *\n   * MOV REG64 temp, IMM64 (EndAddress)\n   *\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetOperandCC : public AutoClone<PatchGenerator, GetOperandCC> {\n  Temp temp;\n  Reg reg;\n  Operand op;\n\n  enum { TmpType, RegType } type;\n\npublic:\n  /*! Obtain the value of the operand op and copy it's value in a temporary. If\n   * op is an immediate the immediate value is copied, if op is a register the\n   * register value is copied.\n   *\n   * @param[in] temp   A temporary where the value will be copied.\n   * @param[in] op     The operand index (relative to the instruction\n   *                   LLVM MCInst representation) to be copied.\n   */\n  GetOperandCC(Temp temp, Operand op)\n      : temp(temp), reg(0), op(op), type(TmpType) {}\n\n  /*! Obtain the value of the operand op and copy it's value in a register. If\n   * op is an immediate the immediate value is copied, if op is a register the\n   * register value is copied.\n   *\n   * @param[in] reg    The register where the value will be copied.\n   * @param[in] op     The operand index (relative to the instruction\n   *                   LLVM MCInst representation) to be copied.\n   */\n  GetOperandCC(Reg reg, Operand op)\n      : temp(0), reg(reg), op(op), type(RegType) {}\n\n  /*!\n   * Output:\n   *   MOV REG64 temp, IMM64/REG64 op\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass CopyRegCC : public AutoClone<PatchGenerator, CopyRegCC> {\n  Reg src;\n  Reg destReg;\n  Temp destTemp;\n\n  enum {\n    Reg2Temp,\n    Reg2Reg,\n  } type;\n\npublic:\n  /*! Copy a register in a temporary.\n   *\n   * @param[in] dest   A temporary where the register will be copied.\n   * @param[in] src    The register which will be copied\n   */\n  CopyRegCC(Temp dest, Reg src)\n      : src(src), destReg(0), destTemp(dest), type(Reg2Temp) {}\n\n  /*! Copy a register in a register.\n   *\n   * @param[in] src    The register which will be copied\n   * @param[in] dest   A register where the register will be copied.\n   */\n  CopyRegCC(Reg dest, Reg src)\n      : src(src), destReg(dest), destTemp(0), type(Reg2Reg) {}\n\n  /*! Output:\n   *\n   * MOV REG64 dest, REG64 src\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass WriteTempCC : public AutoClone<PatchGenerator, WriteTempCC> {\n\n  Temp temp;\n  Offset offset;\n\npublic:\n  /*! Write a temporary value in the data block at the specified offset if the\n   * condition of the instruction is reached. This can be used to overwrite\n   * register values in the context part of the data block.\n   *\n   * @param[in] temp    A temporary which will be written.\n   *                    The value of the temporary is unchanged.\n   * @param[in] offset  The offset in the data block where the temporary\n   *                    will be written.\n   */\n  WriteTempCC(Temp temp, Offset offset) : temp(temp), offset(offset) {}\n\n  /*! Output:\n   *\n   * MOV MEM64 DataBlock[offset], REG64 temp\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override {\n    return offset == Offset(Reg(REG_PC));\n  }\n};\n\nclass WriteOperandCC : public AutoClone<PatchGenerator, WriteOperandCC> {\n  Operand op;\n  Offset offset;\n\npublic:\n  /*! Obtain the value of the operand op and copy it's value to the Datablock\n   * Only Register operand is supported\n   *\n   * @param[in] op      The operand index (relative to the instruction\n   *                    LLVM MCInst representation) to be copied.\n   * @param[in] offset  The offset in the data block where the operand\n   *                    will be written.\n   */\n  WriteOperandCC(Operand op, Offset offset) : op(op), offset(offset) {}\n\n  /*!\n   * Output:\n   *   MOV REG64 temp, IMM64/REG64 op\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override {\n    return offset == Offset(Reg(REG_PC));\n  }\n};\n\nclass CopyTempCC : public AutoClone<PatchGenerator, CopyTempCC> {\n  Temp src;\n  Temp destTemp;\n  Reg destReg;\n\n  enum {\n    Temp2Temp,\n    Temp2Reg,\n  } type;\n\npublic:\n  /*! Copy a temporary in a temporary.\n   *\n   * @param[in] src    The temporary which will be copied\n   * @param[in] dest   A temporary where the temporary will be copied.\n   */\n  CopyTempCC(Temp dest, Temp src)\n      : src(src), destTemp(dest), destReg(0), type(Temp2Temp) {}\n\n  /*! Copy a temporary in a register.\n   *\n   * @param[in] src    The temporary which will be copied\n   * @param[in] dest   A register where the temporary will be copied.\n   */\n  CopyTempCC(Reg dest, Temp src)\n      : src(src), destTemp(0), destReg(dest), type(Temp2Reg) {}\n\n  /*! Output:\n   *\n   * MOV REG64 dest, REG64 src\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass AddOperandToTemp : public AutoClone<PatchGenerator, AddOperandToTemp> {\n  Temp temp;\n  Operand op;\n  Operand op2;\n\npublic:\n  /*! Add the value of two operands and store the result in the temp register\n   *\n   * @param[in] temp   A temporary where the value will be copied.\n   * @param[in] op     The register operand\n   * @param[in] op2     The immediate operand\n   */\n  AddOperandToTemp(Temp temp, Operand op, Operand op2)\n      : temp(temp), op(op), op2(op2) {}\n\n  /*!\n   * Output:\n   *   MOV REG64 temp, IMM64/REG64 op\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass LDMPatchGen : public AutoClone<PatchGenerator, LDMPatchGen> {\n  Temp temp;\n\npublic:\n  /*! A patchGenerator for LDM instruction that used PC\n   *\n   * Only one Temp can be used for all PatchGenerator that used the same\n   * TempManager.\n   */\n  LDMPatchGen(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override { return true; }\n};\n\nclass STMPatchGen : public AutoClone<PatchGenerator, STMPatchGen> {\n  Temp temp;\n\npublic:\n  /*! A patchGenerator for STM instruction that used PC\n   *\n   * Only one Temp can be used for all PatchGenerator that used the same\n   * TempManager.\n   */\n  STMPatchGen(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetReadAddress : public AutoClone<PatchGenerator, GetReadAddress> {\n\n  Temp temp;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the address in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory address will be copied.\n   */\n  GetReadAddress(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   * MOV REG64 temp, REG64 addr\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetWrittenAddress : public AutoClone<PatchGenerator, GetWrittenAddress> {\n\n  Temp temp;\n\npublic:\n  /*! Resolve the memory address where the instructions will write its value and\n   * copy the address in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory address will be copied.\n   */\n  GetWrittenAddress(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * if stack access:\n   * MOV REG64 temp, REG64 addr\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetReadValue : public AutoClone<PatchGenerator, GetReadValue> {\n\n  Temp temp;\n  Temp addr;\n  size_t index;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the value in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory value will be copied.\n   * @param[in] addr      A temporary with the address of the access. If the\n   *                      register is != temp, the address is incremented.\n   *                      (except with size == 3)\n   * @param[in] index     Index of access to saved when access size > 32\n   */\n  GetReadValue(Temp temp, Temp addr, size_t index)\n      : temp(temp), addr(addr), index(index) {}\n\n  /*! Output:\n   *\n   * MOV REG32 temp, MEM32 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetWrittenValue : public AutoClone<PatchGenerator, GetWrittenValue> {\n\n  Temp temp;\n  Temp addr;\n\n  size_t index;\n\npublic:\n  /*! Resolve the memory address where the instructions has written its value\n   * and copy back the value in a temporary. This PatchGenerator is only\n   * guaranteed to work after the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory value will be copied.\n   * @param[in] addr      A temporary with the address of the access. If the\n   *                      register is != temp, the address is incremented.\n   *                      (except with size == 3)\n   * @param[in] index     Index of access to saved when access size > 32\n   */\n  GetWrittenValue(Temp temp, Temp addr, size_t index)\n      : temp(temp), addr(addr), index(index) {}\n\n  /*! Output:\n   *\n   * MOV REG32 temp, MEM32 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass BackupValueX2 : public AutoClone<PatchGenerator, BackupValueX2> {\n\n  Temp temp;\n  Temp temp2;\n  Temp addr;\n  Shadow shadow;\n  Shadow shadow2;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the value in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the first memory value will be\n   *                      copied.\n   * @param[in] temp2     A temporary where the second memory value will be\n   *                      copied.\n   * @param[in] addr      A temporary with the address of the access. (the\n   *                      address is incremented by 8)\n   */\n  BackupValueX2(Temp temp, Temp temp2, Temp addr, Shadow shadow, Shadow shadow2)\n      : temp(temp), temp2(temp2), addr(addr), shadow(shadow), shadow2(shadow2) {\n  }\n\n  /*! Output:\n   *\n   * MOV REG32 temp, MEM32 val\n   * LDMIA addr!, {temp, temp2}\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass CondExclusifLoad : public AutoClone<PatchGenerator, CondExclusifLoad> {\n  Temp temp;\n  Temp temp2;\n\npublic:\n  CondExclusifLoad(Temp temp, Temp temp2) : temp(temp), temp2(temp2) {}\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass SetCondReachAndJump\n    : public AutoUnique<PatchGenerator, SetCondReachAndJump> {\n\n  Temp temp;\n  Shadow tag;\n  PatchGenerator::UniquePtrVec patchVec;\n\npublic:\n  /*! This patch allows to jump over PatchGenerator when an instruction has a\n   * condition and the condition isn't reach at runtime.\n   * If a tag (!= Untagged) is given, the patch will store 0 in the shadow if\n   * the condition isn't reach, else 1. If the instruction isn't conditionnal,\n   * not shadow will be write and no jump will be inserted\n   *\n   * @param[in] temp      A temporary\n   * @param[in] temp2     A temporary where the second memory value will be\n   *                      copied.\n   * @param[in] addr      A temporary with the address of the access. (the\n   *                      address is incremented by 8)\n   */\n  SetCondReachAndJump(Temp temp, Shadow tag,\n                      PatchGenerator::UniquePtrVec &&patchVec)\n      : temp(temp), tag(tag), patchVec(std::move(patchVec)) {}\n\n  PatchGenerator::UniquePtr clone() const override;\n\n  /*! Output:\n   *\n   *   b.<not <cond>> target\n   *   <patchVec>\n   * target:\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass ItPatch : public AutoClone<PatchGenerator, ItPatch> {\n\n  std::array<bool, 4> cond;\n  unsigned nbcond;\n\npublic:\n  /*! Add the conditionnal instruction it. The condition will be the one of the\n   * current instruction.\n   *\n   * /!\\ If used invert a condition, the instruction should never be within a IT\n   * block with the condition AL\n   *\n   * @param[in] cond0   Invert the condition for the first instruction.\n   * @param[in] cond1   Invert the condition for the second instruction.\n   *                    If not given, the ItPatch will cover only 1 instruction.\n   * @param[in] cond2   Invert the condition for the third instruction.\n   *                    If not given, the ItPatch will cover only 2\n   *                    instructions.\n   * @param[in] cond3   Invert the condition for the fourth instruction.\n   *                    If not given, the ItPatch will cover only 3\n   * instructions.\n   */\n  ItPatch(bool cond0) : cond({cond0, false, false, false}), nbcond(1) {}\n  ItPatch(bool cond0, bool cond1)\n      : cond({cond0, cond1, false, false}), nbcond(2) {}\n  ItPatch(bool cond0, bool cond1, bool cond2)\n      : cond({cond0, cond1, cond2, false}), nbcond(3) {}\n  ItPatch(bool cond0, bool cond1, bool cond2, bool cond3)\n      : cond({cond0, cond1, cond2, cond3}), nbcond(4) {}\n\n  /*! Output:\n   *\n   * It<te|te|te> <cond|invcond>\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass TPopPatchGen : public AutoClone<PatchGenerator, TPopPatchGen> {\n  Temp temp;\n\npublic:\n  /*! A patchGenerator for thumb POP (with PC) instruction\n   *\n   * Only one Temp can be used for all PatchGenerator that used the same\n   * TempManager.\n   */\n  TPopPatchGen(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override { return true; }\n};\n\nclass T2LDMPatchGen : public AutoClone<PatchGenerator, T2LDMPatchGen> {\n  Temp temp;\n  bool writePC;\n\npublic:\n  /*! A patchGenerator for thumb2 LDM instruction\n   *\n   * Only one Temp can be used for all PatchGenerator that used the same\n   * TempManager.\n   */\n  T2LDMPatchGen(Temp temp, bool writePC) : temp(temp), writePC(writePC) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override { return writePC; }\n};\n\nclass T2STMPatchGen : public AutoClone<PatchGenerator, T2STMPatchGen> {\n  Temp temp;\n\npublic:\n  /*! A patchGenerator for thumb2 STM instruction\n   *\n   * Only one Temp can be used for all PatchGenerator that used the same\n   * TempManager.\n   */\n  T2STMPatchGen(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass T2TBBTBHPatchGen : public AutoClone<PatchGenerator, T2TBBTBHPatchGen> {\n  Temp temp1;\n  Temp temp2;\n\npublic:\n  /*! A patchGenerator for thumb2 TBB/TBH instruction\n   *\n   * Only one Temp can be used for all PatchGenerator that used the same\n   * TempManager.\n   */\n  T2TBBTBHPatchGen(Temp temp1, Temp temp2) : temp1(temp1), temp2(temp2) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override { return true; }\n};\n\nclass T2BXAUTPatchGen : public AutoClone<PatchGenerator, T2BXAUTPatchGen> {\n\npublic:\n  /*! A patchGenerator for thumb2 BXAUT instruction to perform AUTG operation\n   */\n  T2BXAUTPatchGen() {}\n\n  /*! Output:\n   *\n   * AUTG\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/PatchRuleAssembly_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"ARMInstrInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/ARM/InstInfo_ARM.h\"\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"Patch/ARM/PatchCondition_ARM.h\"\n#include \"Patch/ARM/PatchGenerator_ARM.h\"\n#include \"Patch/ExecBlockFlags.h\"\n#include \"Patch/InstTransform.h\"\n#include \"Patch/PatchRule.h\"\n#include \"Patch/PatchRuleAssembly.h\"\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"QBDI/Options.h\"\n\nnamespace QBDI {\n\nnamespace {\n\nstd::vector<PatchRule> getARMPatchRules(Options opts) {\n  std::vector<PatchRule> rules;\n\n  // instruction where PC can be the first (dest) operand only\n  const Or PCInst1OpDest(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::LDR_PRE_IMM),\n      OpIs::unique(llvm::ARM::LDR_POST_IMM), OpIs::unique(llvm::ARM::MOVi),\n      OpIs::unique(llvm::ARM::MOVi16), OpIs::unique(llvm::ARM::MVNi)));\n\n  // instruction where PC can be the first (src) operand\n  const Or PCInst1OpSrc(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::CMNri), OpIs::unique(llvm::ARM::CMPri),\n      OpIs::unique(llvm::ARM::PLDWi12), OpIs::unique(llvm::ARM::PLDWrs),\n      OpIs::unique(llvm::ARM::PLDi12), OpIs::unique(llvm::ARM::PLDrs),\n      OpIs::unique(llvm::ARM::TEQri), OpIs::unique(llvm::ARM::TSTri),\n      OpIs::unique(llvm::ARM::VLDMDIA), OpIs::unique(llvm::ARM::VLDMSIA),\n      OpIs::unique(llvm::ARM::FLDMXIA), OpIs::unique(llvm::ARM::VSTMDIA),\n      OpIs::unique(llvm::ARM::VSTMSIA), OpIs::unique(llvm::ARM::FSTMXIA)));\n\n  // instruction where PC can be the second (src) operand\n  const Or PCInst1OpSrcOff1(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::LDRBi12), OpIs::unique(llvm::ARM::LDRBrs),\n      OpIs::unique(llvm::ARM::LDRH), OpIs::unique(llvm::ARM::LDRSB),\n      OpIs::unique(llvm::ARM::LDRSH), OpIs::unique(llvm::ARM::STRBi12),\n      OpIs::unique(llvm::ARM::STRBrs), OpIs::unique(llvm::ARM::STRH),\n      OpIs::unique(llvm::ARM::STRT_POST_IMM), OpIs::unique(llvm::ARM::VLDRD),\n      OpIs::unique(llvm::ARM::VLDRH), OpIs::unique(llvm::ARM::VLDRS),\n      OpIs::unique(llvm::ARM::VSTRD), OpIs::unique(llvm::ARM::VSTRH),\n      OpIs::unique(llvm::ARM::VSTRS)));\n\n  // instruction where PC can be the third (src) operand\n  const Or PCInst1OpSrcOff2(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::LDRD), OpIs::unique(llvm::ARM::STC_OFFSET),\n      OpIs::unique(llvm::ARM::STRD), OpIs::unique(llvm::ARM::LDC2L_OFFSET),\n      OpIs::unique(llvm::ARM::LDC2L_OPTION),\n      OpIs::unique(llvm::ARM::LDC2L_POST), OpIs::unique(llvm::ARM::LDC2L_PRE),\n      OpIs::unique(llvm::ARM::LDC2_OFFSET),\n      OpIs::unique(llvm::ARM::LDC2_OPTION), OpIs::unique(llvm::ARM::LDC2_POST),\n      OpIs::unique(llvm::ARM::LDC2_PRE), OpIs::unique(llvm::ARM::LDCL_OFFSET),\n      OpIs::unique(llvm::ARM::LDCL_OPTION), OpIs::unique(llvm::ARM::LDCL_POST),\n      OpIs::unique(llvm::ARM::LDCL_PRE), OpIs::unique(llvm::ARM::LDC_OFFSET),\n      OpIs::unique(llvm::ARM::LDC_OPTION), OpIs::unique(llvm::ARM::LDC_POST),\n      OpIs::unique(llvm::ARM::LDC_PRE)));\n\n  // instruction where PC can be the first/second (src) operands\n  const Or PCInst2OpSrc(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::CMNzrr), OpIs::unique(llvm::ARM::CMNzrsi),\n      OpIs::unique(llvm::ARM::CMPrr), OpIs::unique(llvm::ARM::STRi12),\n      OpIs::unique(llvm::ARM::STRrs), OpIs::unique(llvm::ARM::TEQrr),\n      OpIs::unique(llvm::ARM::TSTrr)));\n\n  // instruction where PC can be the first (dest) or/and the second (src)\n  // operands only\n  const Or PCInst2Op(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::ADCri), OpIs::unique(llvm::ARM::ADDri),\n      OpIs::unique(llvm::ARM::ANDri), OpIs::unique(llvm::ARM::BICri),\n      OpIs::unique(llvm::ARM::EORri), OpIs::unique(llvm::ARM::LDRi12),\n      OpIs::unique(llvm::ARM::LDRrs), OpIs::unique(llvm::ARM::MOVr),\n      OpIs::unique(llvm::ARM::MOVsi), OpIs::unique(llvm::ARM::MVNr),\n      OpIs::unique(llvm::ARM::ORRri), OpIs::unique(llvm::ARM::RSBri),\n      OpIs::unique(llvm::ARM::RSCri), OpIs::unique(llvm::ARM::SBCri),\n      OpIs::unique(llvm::ARM::SUBri)));\n\n  // instruction where PC can be the first (dest) or/and the second/third (src)\n  // operands only\n  const Or PCInst3Op(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::ADCrr), OpIs::unique(llvm::ARM::ADCrsi),\n      OpIs::unique(llvm::ARM::ADDrr), OpIs::unique(llvm::ARM::ADDrsi),\n      OpIs::unique(llvm::ARM::ANDrr), OpIs::unique(llvm::ARM::ANDrsi),\n      OpIs::unique(llvm::ARM::BICrr), OpIs::unique(llvm::ARM::BICrsi),\n      OpIs::unique(llvm::ARM::EORrr), OpIs::unique(llvm::ARM::EORrsi),\n      OpIs::unique(llvm::ARM::ORRrr), OpIs::unique(llvm::ARM::ORRrsi),\n      OpIs::unique(llvm::ARM::RSBrr), OpIs::unique(llvm::ARM::RSBrsi),\n      OpIs::unique(llvm::ARM::RSCrr), OpIs::unique(llvm::ARM::RSCrsi),\n      OpIs::unique(llvm::ARM::SBCrr), OpIs::unique(llvm::ARM::SBCrsi),\n      OpIs::unique(llvm::ARM::SUBrr), OpIs::unique(llvm::ARM::SUBrsi)));\n\n  // ARM instruction\n  // ===============\n\n  // Warning: All instructions can be conditionnal, Each patchRule must keep the\n  // conditionnal behavior\n\n  /* Rule #0: BX lr | mov pc, lr without condition\n   *\n   *  str lr, <offset PC>\n   *  setExchange\n   */\n  rules.emplace_back(And::unique(conv_unique<PatchCondition>(\n                         Or::unique(conv_unique<PatchCondition>(\n                             OpIs::unique(llvm::ARM::BX_RET),\n                             OpIs::unique(llvm::ARM::MOVPCLR))),\n                         Not::unique(HasCond::unique()))),\n                     conv_unique<PatchGenerator>(\n                         SaveReg::unique(Reg(REG_LR), Offset(Reg(REG_PC))),\n                         SetExchange::unique(Temp(0))));\n\n  /* Rule #1: BX lr | mov pc, lr with condition\n   *\n   *  mov temp0, <PC-4>\n   *  movcc temp0, lr\n   *  str temp0, <offset PC>\n   *  setExchange\n   */\n  rules.emplace_back(\n      And::unique(\n          conv_unique<PatchCondition>(Or::unique(conv_unique<PatchCondition>(\n                                          OpIs::unique(llvm::ARM::BX_RET),\n                                          OpIs::unique(llvm::ARM::MOVPCLR))),\n                                      HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          CopyRegCC::unique(Temp(0), Reg(REG_LR)), WritePC::unique(Temp(0)),\n          SetExchange::unique(Temp(0))));\n\n  /* Rule #2: BX pc without condition\n   *\n   *  mov temp0, <PC>\n   *  str temp0, <offset PC>\n   *  // setExchange not needed as PC will be aligned\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::BX), OpIs::unique(llvm::ARM::BX_pred))),\n          OperandIs::unique(0, Reg(REG_PC)), Not::unique(HasCond::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #3: BX pc with condition\n   *\n   *  mov temp0, <PC-4>\n   *  movcc temp0, <PC>\n   *  str temp0, <offset PC>\n   *  // setExchange not needed as PC will be aligned\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::BX), OpIs::unique(llvm::ARM::BX_pred))),\n          OperandIs::unique(0, Reg(REG_PC)), HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ true),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #4: BX reg without condition\n   *\n   *  mov temp0, reg\n   *  str temp0, <offset PC>\n   *  setExchange\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::BX), OpIs::unique(llvm::ARM::BX_pred))),\n          Not::unique(HasCond::unique()))),\n      conv_unique<PatchGenerator>(GetOperand::unique(Temp(0), Operand(0)),\n                                  WritePC::unique(Temp(0)),\n                                  SetExchange::unique(Temp(0))));\n\n  /* Rule #5: BX reg with condition\n   *\n   *  mov temp0, <PC-4>\n   *  movcc temp0, reg\n   *  str temp0, <offset PC>\n   *  setExchange\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::BX), OpIs::unique(llvm::ARM::BX_pred))),\n          HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetOperandCC::unique(Temp(0), Operand(0)), WritePC::unique(Temp(0)),\n          SetExchange::unique(Temp(0))));\n\n  /* Rule #6: BLX reg\n   *\n   *  mov temp0, <PC-4>\n   *  movcc temp0, reg\n   *  str temp0, <offset PC>\n   *  movcc lr, <PC-4>\n   *  setExchange\n   *\n   * note: BLX LR is possible\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::BLX), OpIs::unique(llvm::ARM::BLX_pred))),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetOperandCC::unique(Temp(0), Operand(0)), WritePC::unique(Temp(0)),\n          SetExchange::unique(Temp(0)),\n          GetNextInstAddr::unique(Reg(REG_LR), /* keepCond */ true)));\n\n  /* Rule #7: BLX imm\n   *\n   * never cond\n   *\n   *  mov temp0, <PC-4>\n   *  movcc lr, temp0\n   *  movcc temp0, <PC+Operand(0)>\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      OpIs::unique(llvm::ARM::BLXi),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Reg(REG_LR), /* keepCond */ false),\n          GetPCOffset::unique(Temp(0), Operand(0), /* keepCond */ false),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #8: BL imm\n   *\n   *  mov temp0, <PC-4>\n   *  movcc lr, temp0\n   *  movcc temp0, <PC+Operand(0)>\n   *  str temp0, <offset PC>\n   *\n   *    no SetExchange, as BL doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::BL),\n                                             OpIs::unique(llvm::ARM::BL_pred))),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          CopyTempCC::unique(Reg(REG_LR), Temp(0)),\n          GetPCOffset::unique(Temp(0), Operand(0), /* keepCond */ true),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #9: Bcc with cond\n   * - bcc #imm\n   *\n   *  mov temp0, <PC-4>\n   *  movcc temp0, <PC + imm>\n   *  str temp0, <offset PC>\n   *\n   *    no SetExchange, as Bcc doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::Bcc),\n                                              HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetPCOffset::unique(Temp(0), Operand(0), /* keepCond */ true),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #10: Bcc without cond\n   * - b #imm\n   *\n   *  mov temp0, <PC + imm>\n   *  str temp0, <offset PC>\n   *\n   *    no SetExchange, as Bcc doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::Bcc),\n                                              Not::unique(HasCond::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(0), /* keepCond */ false),\n          WritePC::unique(Temp(0))));\n\n  // local monitor\n  // =============\n\n  if ((opts & Options::OPT_DISABLE_LOCAL_MONITOR) == 0) {\n\n    /* Rule #11: Clear local monitor state\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::ARM::CLREX),\n        conv_unique<PatchGenerator>(\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable)))));\n\n    /* Rule #12: Clear local monitor state\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::ARM::SVC),\n        conv_unique<PatchGenerator>(\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            // for SVC, we need to backup the value of Temp(0) after the syscall\n            SaveTemp::unique(Temp(0), true),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable)))));\n\n    /* Rule #13: exclusive load 1 register\n     */\n    rules.emplace_back(\n        Or::unique(conv_unique<PatchCondition>(\n            OpIs::unique(llvm::ARM::LDREX), OpIs::unique(llvm::ARM::LDREXB),\n            OpIs::unique(llvm::ARM::LDREXD), OpIs::unique(llvm::ARM::LDREXH))),\n        conv_unique<PatchGenerator>(\n            GetConstantMap::unique(Temp(0),\n                                   std::map<unsigned, Constant>({\n                                       {llvm::ARM::LDREXB, Constant(1)},\n                                       {llvm::ARM::LDREXH, Constant(2)},\n                                       {llvm::ARM::LDREX, Constant(4)},\n                                       {llvm::ARM::LDREXD, Constant(8)},\n                                   })),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            WriteOperandCC::unique(\n                Operand(1),\n                Offset(offsetof(Context, gprState.localMonitor.addr))),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec())));\n\n    /* Rule #14: exclusive store\n     */\n    rules.emplace_back(\n        Or::unique(conv_unique<PatchCondition>(\n            OpIs::unique(llvm::ARM::STREX), OpIs::unique(llvm::ARM::STREXB),\n            OpIs::unique(llvm::ARM::STREXD), OpIs::unique(llvm::ARM::STREXH))),\n        conv_unique<PatchGenerator>(\n            CondExclusifLoad::unique(Temp(0), Temp(1)),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable)))));\n  }\n\n  // Instruction without PC\n  // ======================\n\n  /* Rule #15: instruction to skip (barrier, preload)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::PLDWi12), OpIs::unique(llvm::ARM::PLDWrs),\n          OpIs::unique(llvm::ARM::PLDi12), OpIs::unique(llvm::ARM::PLDrs),\n          OpIs::unique(llvm::ARM::PLIi12), OpIs::unique(llvm::ARM::PLIrs))),\n      PatchGenerator::UniquePtrVec());\n\n  /* Rule #16: all other instruction without PC\n   *\n   * Note: This patch should be at the end of the list. However, as many\n   * instruction doesn't used PC, we place it here to apply it early on\n   * intructions without PC.\n   */\n  rules.emplace_back(\n      Not::unique(Or::unique(conv_unique<PatchCondition>(\n          UseReg::unique(Reg(REG_PC)),\n          // ADR is decoded as ADDri\n          OpIs::unique(llvm::ARM::ADR),\n          // unsupported instruction\n          OpIs::unique(llvm::ARM::SETEND), OpIs::unique(llvm::ARM::BXJ)))),\n      conv_unique<PatchGenerator>(\n          ModifyInstruction::unique(InstTransform::UniquePtrVec())));\n\n  // Instruction with PC : special case LDM / STM\n  // ============================================\n\n  /* Rule #17: LDM with PC\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          UseReg::unique(Reg(REG_PC)),\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::LDMIA), OpIs::unique(llvm::ARM::LDMIB),\n              OpIs::unique(llvm::ARM::LDMDA), OpIs::unique(llvm::ARM::LDMDB),\n              OpIs::unique(llvm::ARM::LDMIA_UPD),\n              OpIs::unique(llvm::ARM::LDMIB_UPD),\n              OpIs::unique(llvm::ARM::LDMDA_UPD),\n              OpIs::unique(llvm::ARM::LDMDB_UPD))))),\n      conv_unique<PatchGenerator>(LDMPatchGen::unique(Temp(0)),\n                                  SetExchange::unique(Temp(0))));\n\n  /* Rule #18: STM with PC\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          UseReg::unique(Reg(REG_PC)),\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::STMIA), OpIs::unique(llvm::ARM::STMIB),\n              OpIs::unique(llvm::ARM::STMDA), OpIs::unique(llvm::ARM::STMDB),\n              OpIs::unique(llvm::ARM::STMIA_UPD),\n              OpIs::unique(llvm::ARM::STMIB_UPD),\n              OpIs::unique(llvm::ARM::STMDA_UPD),\n              OpIs::unique(llvm::ARM::STMDB_UPD))))),\n      conv_unique<PatchGenerator>(STMPatchGen::unique(Temp(0))));\n\n  // Instruction with PC as source\n  // =============================\n\n  /* Rule #19: ADD/SUB/... with PC as source only:\n   * - both in second and third operand (src only)\n   * - both in first and third operand (src only)\n   * - in first (src) operand\n   * - in second (src) operand\n   * - in third (src) operand\n   *\n   *  mov temp0, <PC>\n   *  add r12, temp0, temp0\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          And::unique(conv_unique<PatchCondition>(\n              Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n              OperandIs::unique(1, Reg(REG_PC)),\n              OperandIs::unique(2, Reg(REG_PC)), PCInst3Op.clone())),\n          And::unique(conv_unique<PatchCondition>(\n              OperandIs::unique(0, Reg(REG_PC)),\n              OperandIs::unique(1, Reg(REG_PC)),\n              Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n              PCInst2OpSrc.clone())),\n          And::unique(conv_unique<PatchCondition>(\n              OperandIs::unique(0, Reg(REG_PC)),\n              Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n              Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n              Or::unique(conv_unique<PatchCondition>(PCInst2OpSrc.clone(),\n                                                     PCInst1OpSrc.clone())))),\n          And::unique(conv_unique<PatchCondition>(\n              Not::unique(OperandIs::unique(0, Reg(REG_PC))),\n              OperandIs::unique(1, Reg(REG_PC)),\n              Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n              Or::unique(conv_unique<PatchCondition>(\n                  PCInst1OpSrcOff1.clone(), PCInst2OpSrc.clone(),\n                  PCInst2Op.clone(), PCInst3Op.clone())))),\n          And::unique(conv_unique<PatchCondition>(\n              Not::unique(OperandIs::unique(0, Reg(REG_PC))),\n              Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n              OperandIs::unique(2, Reg(REG_PC)),\n              Or::unique(conv_unique<PatchCondition>(PCInst1OpSrcOff2.clone(),\n                                                     PCInst3Op.clone())))))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0))))));\n\n  // Instruction with PC 3 times and dest\n  // ====================================\n\n  /* Rule #20: LDR/ADD/SUB/... with PC in first (dst) and second/third (src)\n   * operand and without cond\n   *  - add pc, pc, pc\n   *\n   *  mov temp0, <PC>\n   *  add temp0, temp0, temp0\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)), OperandIs::unique(1, Reg(REG_PC)),\n          OperandIs::unique(2, Reg(REG_PC)), PCInst3Op.clone(),\n          Not::unique(HasCond::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #21: LDR/ADD/SUB/... with PC in first (dst) and second/third (src)\n   * operand and with cond\n   *  - addcc pc, pc, pc\n   *\n   *  mov temp0, <PC-4>\n   *  mov temp1, <PC>\n   *  addcc temp0, temp1, temp1\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)), OperandIs::unique(1, Reg(REG_PC)),\n          OperandIs::unique(2, Reg(REG_PC)), PCInst3Op.clone(),\n          HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetPCOffset::unique(Temp(1), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(0), Temp(0)),\n              SetOperand::unique(Operand(1), Temp(1)),\n              SetOperand::unique(Operand(2), Temp(1)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  // Instruction with PC 2 times and dest\n  // ====================================\n\n  /* Rule #22: ADD/SUB/... with PC in first (dst) and third (src) operand and\n   * without cond\n   *  - add pc, r12, pc\n   *\n   *  mov temp0, <PC>\n   *  add temp0, r12, temp0\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n          OperandIs::unique(2, Reg(REG_PC)), PCInst3Op.clone(),\n          Not::unique(HasCond::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #23: ADD/SUB/... with PC in first (dst) and third (src) operand and\n   * with cond\n   *  - addcc pc, r12, pc\n   *\n   *  mov temp0, <PC-4>\n   *  mov temp1, <PC>\n   *  addcc temp0, r12, temp1\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n          OperandIs::unique(2, Reg(REG_PC)), PCInst3Op.clone(),\n          HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetPCOffset::unique(Temp(1), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(0), Temp(0)),\n              SetOperand::unique(Operand(2), Temp(1)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #24: LDR/ADD/SUB/... with PC in first (dst) and second (src) operand\n   * and without cond\n   *  - ldr pc, [pc, #0x80]\n   *\n   *  mov temp0, <PC>\n   *  ldr temp0, [temp0, #0x80]\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)), OperandIs::unique(1, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n          Or::unique(conv_unique<PatchCondition>(PCInst3Op.clone(),\n                                                 PCInst2Op.clone())),\n          Not::unique(HasCond::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #25: LDR/ADD/SUB/... with PC in first (dst) and second (src) operand\n   * and with cond\n   *  - ldrcc pc, [pc, #0x80]\n   *\n   *  mov temp0, <PC-4>\n   *  mov temp1, <PC>\n   *  ldrcc temp0, [temp1, #0x80]\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)), OperandIs::unique(1, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n          Or::unique(conv_unique<PatchCondition>(PCInst3Op.clone(),\n                                                 PCInst2Op.clone())),\n          HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetPCOffset::unique(Temp(1), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(0), Temp(0)),\n              SetOperand::unique(Operand(1), Temp(1)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  // Instruction with PC 1 times and dst\n  // ===================================\n\n  /* Rule #26: LDR/ADD/SUB/... with PC in first (dst) operand and without cond\n   *  - ldr pc, [r0, #0x80]\n   *\n   *  ldr temp0, [r0, #0x80]\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n          Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n          Not::unique(HasCond::unique()),\n          Or::unique(conv_unique<PatchCondition>(\n              PCInst1OpDest.clone(), PCInst2Op.clone(), PCInst3Op.clone())))),\n      conv_unique<PatchGenerator>(\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #27: LDR/ADD/SUB/... with PC in first (dst) operand and with cond\n   *  - ldrcc pc, [r0, #0x80]\n   *\n   *  mov temp0, <PC-4>\n   *  ldrcc temp0, [r0, #0x80]\n   *  str temp0, <offset PC>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n          Not::unique(OperandIs::unique(2, Reg(REG_PC))), HasCond::unique(),\n          Or::unique(conv_unique<PatchCondition>(\n              PCInst1OpDest.clone(), PCInst2Op.clone(), PCInst3Op.clone())),\n          HasCond::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  return rules;\n}\n\nstd::vector<PatchRule> getThumbPatchRules(Options opts) {\n  std::vector<PatchRule> rules;\n\n  // Warning about Thumb Patch\n  // -> an ITBlock can have the condition AL:\n  //    -> we should keep the ITBlock of the instruction to keep the same flags\n  //       behavior. We can only remove the ITBlock with AL condition on\n  //       instruction that have the same semantique outside or inside the AL\n  //       ITBlock\n  //    -> For any other instruction, we cannot inverse the condition\n  //       (as `inv(AL)` is invalid).\n  // -> Be carefull about PC behavior:\n  //    -> when PC is used as a source, some instruction used `Align(PC, 4)`\n  //       instead of PC\n  //    -> In thumb, PC is always `instAddress + 4`, regardless of the size\n  //       of the instruction\n  //    -> When the instruction sets PC, be carefull if the instruction can\n  //       change the CPU mode. QBDI has two mechanism that must be used\n  //       together:\n  //       -> When the instruction can change the mode, SetExchange must be set.\n  //          The new mode will be the LSB of PC.\n  //       -> otherwise, the LSB bit **must** be set to 1. If the user returns\n  //          BREAK_TO_VM in a callback after the instruction, we want the LSB\n  //          to be correctly set.\n  // -> Some instruction inside an ITBlock can change the flags (CMP, CMN, TST,\n  //    ...):\n  //    -> The new flags is used for the next instruction on the same ITBlock.\n  //       We can therefore safely split an ITBlock.\n  //    -> Be carefull when patch an instruction inside an ITBlock. The flags\n  //       may be change after the `ModifyInstruction`.\n  //    -> note that no instruction seems be able to set PC and the flags at the\n  //       same time. All instructions with ALUWritePC doesn't set the flags if\n  //       PC is the destination register.\n\n  // instruction where PC can be the first (dest) operand only\n  const Or PCInst1OpDest(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::t2LDRi8), OpIs::unique(llvm::ARM::t2LDR_PRE),\n      OpIs::unique(llvm::ARM::t2LDR_POST), OpIs::unique(llvm::ARM::t2LDRi12),\n      OpIs::unique(llvm::ARM::t2LDRs)));\n\n  // instruction where PC can be the second (src) operand\n  const Or PCInst1OpSrcOff1(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::VLDRD), OpIs::unique(llvm::ARM::VLDRH),\n      OpIs::unique(llvm::ARM::VLDRS)));\n\n  // instruction where PC can be the third (src) operand\n  const Or PCInst1OpSrcOff2(conv_unique<PatchCondition>(\n      OpIs::unique(llvm::ARM::t2LDC2L_OFFSET),\n      OpIs::unique(llvm::ARM::t2LDC2_OFFSET),\n      OpIs::unique(llvm::ARM::t2LDCL_OFFSET),\n      OpIs::unique(llvm::ARM::t2LDC_OFFSET), OpIs::unique(llvm::ARM::t2LDRDi8),\n      OpIs::unique(llvm::ARM::tADDspr)));\n\n  // instruction where PC can be the first (dest) or/and the second (src)\n  // operands only\n  const OpIs PCInst2Op(llvm::ARM::tMOVr);\n\n  // instruction where PC can be the first (dest) AND the third (src) operands\n  // the first and third operand is always the same\n  const OpIs PCInst1Dst3Src(llvm::ARM::tADDrSP);\n\n  // instruction where PC can be the first|second (dest/src) OR the third (src)\n  // operands\n  // the first and second operand is always the same\n  const OpIs PCInst1Dst2SrcOR3Src(llvm::ARM::tADDhirr);\n\n  // Thumb instruction\n  // =================\n\n  /* Rule #0: Adr <rx>, <imm>\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::tADR),\n                                             OpIs::unique(llvm::ARM::t2ADR))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Operand(0), Operand(1), /* keepCond */ true)));\n\n  /* Rule #1: Bcc <imm>\n   *\n   *    no SetExchange, as Bcc doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          And::unique(conv_unique<PatchCondition>(\n              Or::unique(\n                  conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::tBcc),\n                                              OpIs::unique(llvm::ARM::t2Bcc))),\n              Not::unique(InITBlock::unique()))),\n          And::unique(conv_unique<PatchCondition>(\n              Or::unique(conv_unique<PatchCondition>(\n                  OpIs::unique(llvm::ARM::tB), OpIs::unique(llvm::ARM::t2B))),\n              LastInITBlock::unique())))),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetPCOffset::unique(Temp(0), Operand(0), /* keepCond */ true),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #2: B <imm>\n   *\n   *    no SetExchange, as B doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::tB),\n                                                 OpIs::unique(llvm::ARM::t2B))),\n          Not::unique(InITBlock::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(0), /* keepCond */ false),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #3: BL <imm>\n   *\n   *    no SetExchange, as BL doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::tBL),\n          Or::unique(conv_unique<PatchCondition>(\n              Not::unique(InITBlock::unique()), LastInITBlock::unique())))),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          CopyTempCC::unique(Reg(REG_LR), Temp(0)),\n          GetPCOffset::unique(Temp(0), Operand(2), /* keepCond */ true),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #4: BLX <imm>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::tBLXi),\n          Or::unique(conv_unique<PatchCondition>(\n              Not::unique(InITBlock::unique()), LastInITBlock::unique())))),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          CopyTempCC::unique(Reg(REG_LR), Temp(0)),\n          GetPCOffset::unique(Temp(0), Operand(2), /* keepCond */ true),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #5: BLX <reg>\n   *\n   * note: BLX LR is possible\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::tBLXr),\n          Or::unique(conv_unique<PatchCondition>(\n              Not::unique(InITBlock::unique()), LastInITBlock::unique())))),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetOperandCC::unique(Temp(0), Operand(2)), WritePC::unique(Temp(0)),\n          SetExchange::unique(Temp(0)),\n          GetNextInstAddr::unique(Reg(REG_LR), /* keepCond */ true)));\n\n  /* Rule #6: BXcc pc\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::tBX),\n                                              OperandIs::unique(0, Reg(REG_PC)),\n                                              LastInITBlock::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ true),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #7: BX pc\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::tBX), OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(InITBlock::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #8: BXcc <reg>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::tBX),\n                                              LastInITBlock::unique())),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetOperandCC::unique(Temp(0), Operand(0)), WritePC::unique(Temp(0)),\n          SetExchange::unique(Temp(0))));\n\n  /* Rule #9: BX <reg>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::tBX), Not::unique(InITBlock::unique()))),\n      conv_unique<PatchGenerator>(GetOperand::unique(Temp(0), Operand(0)),\n                                  WritePC::unique(Temp(0)),\n                                  SetExchange::unique(Temp(0))));\n\n  /* Rule #10: bxaut <reg>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::t2BXAUT),\n                                              LastInITBlock::unique())),\n      conv_unique<PatchGenerator>(\n          T2BXAUTPatchGen::unique(), GetOperand::unique(Temp(0), Operand(2)),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #11: bxaut <reg>\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::t2BXAUT), Not::unique(InITBlock::unique()))),\n      conv_unique<PatchGenerator>(\n          T2BXAUTPatchGen::unique(),\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          GetOperandCC::unique(Temp(0), Operand(2)), WritePC::unique(Temp(0)),\n          SetExchange::unique(Temp(0))));\n\n  /* Rule #12: CBZ|CBNZ <reg>, imm\n   *\n   *    no SetExchange, as CBZ|CBNZ doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::tCBNZ), OpIs::unique(llvm::ARM::tCBZ))),\n          Not::unique(InITBlock::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(1), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(1), Constant(2)))),\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          WritePC::unique(Temp(0))));\n\n  /* Rule #13: TBB|TBH [<reg>, <reg>{, LSL #1}]\n   *\n   *    no SetExchange, as TBB|TBH doesn't perform the change of mode\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::t2TBB), OpIs::unique(llvm::ARM::t2TBH))),\n          Or::unique(conv_unique<PatchCondition>(\n              Not::unique(InITBlock::unique()), LastInITBlock::unique())))),\n      conv_unique<PatchGenerator>(T2TBBTBHPatchGen::unique(Temp(0), Temp(1))));\n\n  /* Rule #14: LDR <reg>, [pc, #<imm>]\n   */\n  rules.emplace_back(\n      OpIs::unique(llvm::ARM::tLDRpci),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ItPatch::unique(false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOpcode::unique(llvm::ARM::t2LDRi12),\n              AddOperand::unique(Operand(1), Temp(0))))));\n\n  /* Rule #15: LDR.w pc, [pc, #<imm>]\n   * not in ITBlock\n   *\n   * need specific rules, because the immediate size is 12 bits when PC, but\n   * can be limited to 8 bits when anothers register is used as a source\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::t2LDRpci), OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(InITBlock::unique()))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(1), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOpcode::unique(llvm::ARM::t2LDRi12),\n              SetOperand::unique(Operand(0), Temp(0)),\n              AddOperand::unique(Operand(1), Temp(0)),\n              SetOperand::unique(Operand(2), Constant(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #16: LDR.w pc, [pc, #<imm>]\n   * Last in ITBlock\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::t2LDRpci),\n                                              OperandIs::unique(0, Reg(REG_PC)),\n                                              LastInITBlock::unique())),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(1), /* keepCond */ false),\n          ItPatch::unique(false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOpcode::unique(llvm::ARM::t2LDRi12),\n              SetOperand::unique(Operand(0), Temp(0)),\n              AddOperand::unique(Operand(1), Temp(0)),\n              SetOperand::unique(Operand(2), Constant(0)))),\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ true,\n                                  /* invCond */ true),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #17: LDR.w <reg>, [pc, #<imm>]\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Or::unique(\n              conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::t2LDRpci),\n                                          OpIs::unique(llvm::ARM::t2LDRHpci),\n                                          OpIs::unique(llvm::ARM::t2LDRBpci),\n                                          OpIs::unique(llvm::ARM::t2LDRSHpci),\n                                          OpIs::unique(llvm::ARM::t2LDRSBpci))),\n          Not::unique(OperandIs::unique(0, Reg(REG_PC))))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(1), /* keepCond */ false),\n          ItPatch::unique(false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              ReplaceOpcode::unique(std::map<unsigned, unsigned>({\n                  {llvm::ARM::t2LDRpci, llvm::ARM::t2LDRi12},\n                  {llvm::ARM::t2LDRBpci, llvm::ARM::t2LDRBi12},\n                  {llvm::ARM::t2LDRHpci, llvm::ARM::t2LDRHi12},\n                  {llvm::ARM::t2LDRSBpci, llvm::ARM::t2LDRSBi12},\n                  {llvm::ARM::t2LDRSHpci, llvm::ARM::t2LDRSHi12},\n              })),\n              AddOperand::unique(Operand(1), Temp(0)),\n              SetOperand::unique(Operand(2), Constant(0))))));\n\n  // Instruction with PC 2 times\n  // ===========================\n  //\n\n  /* Rule #18:\n   * - PC in the first (dest) and third operand (src)\n   * - PC in the first (dest) and second operand (src)\n   * not in ITBlock\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)), Not::unique(InITBlock::unique()),\n          Or::unique(conv_unique<PatchCondition>(\n              And::unique(conv_unique<PatchCondition>(\n                  Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n                  OperandIs::unique(2, Reg(REG_PC)), PCInst1Dst3Src.clone())),\n              And::unique(conv_unique<PatchCondition>(\n                  OperandIs::unique(1, Reg(REG_PC)),\n                  Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n                  Or::unique(conv_unique<PatchCondition>(\n                      PCInst1Dst2SrcOR3Src.clone(), PCInst2Op.clone())))))))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #19:\n   * - PC in the first (dest) and third operand (src)\n   * - PC in the first (dest) and second operand (src)\n   * Last in ITBlock\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)), LastInITBlock::unique(),\n          Or::unique(conv_unique<PatchCondition>(\n              And::unique(conv_unique<PatchCondition>(\n                  Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n                  OperandIs::unique(2, Reg(REG_PC)), PCInst1Dst3Src.clone())),\n              And::unique(conv_unique<PatchCondition>(\n                  OperandIs::unique(1, Reg(REG_PC)),\n                  Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n                  Or::unique(conv_unique<PatchCondition>(\n                      PCInst1Dst2SrcOR3Src.clone(), PCInst2Op.clone())))))))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ItPatch::unique(false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)))),\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ true,\n                                  /* invCond */ true),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  // Instruction with PC 1 time\n  // ==========================\n  //\n  /* Rule #20: ADD... with PC in the first (dest)\n   * not in ITBlock\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n          Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n          Not::unique(InITBlock::unique()),\n          Or::unique(conv_unique<PatchCondition>(PCInst2Op.clone(),\n                                                 PCInst1OpDest.clone())))),\n      conv_unique<PatchGenerator>(\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(0), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #21: ADD... with PC in the first (dest)\n   * Last in ITBlock\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OperandIs::unique(0, Reg(REG_PC)),\n          Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n          Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n          LastInITBlock::unique(),\n          Or::unique(conv_unique<PatchCondition>(PCInst2Op.clone(),\n                                                 PCInst1OpDest.clone())))),\n      conv_unique<PatchGenerator>(\n          GetNextInstAddr::unique(Temp(0), /* keepCond */ false),\n          ItPatch::unique(false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(0), Temp(0)))),\n          WritePC::unique(Temp(0)), SetExchange::unique(Temp(0))));\n\n  /* Rule #22:\n   * - PC in the second operand (src only)\n   * - PC in the third operand (src only)\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Not::unique(OperandIs::unique(0, Reg(REG_PC))),\n          Or::unique(conv_unique<PatchCondition>(\n              And::unique(conv_unique<PatchCondition>(\n                  OperandIs::unique(1, Reg(REG_PC)),\n                  Not::unique(OperandIs::unique(2, Reg(REG_PC))),\n                  Or::unique(conv_unique<PatchCondition>(\n                      PCInst2Op.clone(), PCInst1OpSrcOff1.clone())))),\n              And::unique(conv_unique<PatchCondition>(\n                  Not::unique(OperandIs::unique(1, Reg(REG_PC))),\n                  OperandIs::unique(2, Reg(REG_PC)),\n                  Or::unique(conv_unique<PatchCondition>(\n                      PCInst1Dst2SrcOR3Src.clone(),\n                      PCInst1OpSrcOff2.clone())))))))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0), /* keepCond */ false),\n          ItPatch::unique(false),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0))))));\n\n  // Special case: LDM POP PUSH STM\n  // ==============================\n\n  /* Rule #23: LDM with PC\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          UseReg::unique(Reg(REG_PC)),\n          Or::unique(conv_unique<PatchCondition>(\n              Not::unique(InITBlock::unique()), LastInITBlock::unique())),\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::t2LDMIA),\n              OpIs::unique(llvm::ARM::t2LDMDB),\n              OpIs::unique(llvm::ARM::t2LDMIA_UPD),\n              OpIs::unique(llvm::ARM::t2LDMDB_UPD))))),\n      conv_unique<PatchGenerator>(T2LDMPatchGen::unique(Temp(0), true),\n                                  SetExchange::unique(Temp(0))));\n  /* Rule #24: LDM without PC\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Not::unique(UseReg::unique(Reg(REG_PC))),\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::t2LDMIA),\n              OpIs::unique(llvm::ARM::t2LDMDB),\n              OpIs::unique(llvm::ARM::t2LDMIA_UPD),\n              OpIs::unique(llvm::ARM::t2LDMDB_UPD))))),\n      conv_unique<PatchGenerator>(T2LDMPatchGen::unique(Temp(0), false)));\n\n  /* Rule #25: POP with PC\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::tPOP), UseReg::unique(Reg(REG_PC)),\n          Or::unique(conv_unique<PatchCondition>(\n              Not::unique(InITBlock::unique()), LastInITBlock::unique())))),\n      conv_unique<PatchGenerator>(TPopPatchGen::unique(Temp(0)),\n                                  SetExchange::unique(Temp(0))));\n\n  /* Rule #26: STM\n   *\n   * Note: PC and SP cannot be store in Thumb mode\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(\n          Not::unique(UseReg::unique(Reg(REG_PC))),\n          Or::unique(conv_unique<PatchCondition>(\n              OpIs::unique(llvm::ARM::t2STMIA),\n              OpIs::unique(llvm::ARM::t2STMDB),\n              OpIs::unique(llvm::ARM::t2STMIA_UPD),\n              OpIs::unique(llvm::ARM::t2STMDB_UPD))))),\n      conv_unique<PatchGenerator>(T2STMPatchGen::unique(Temp(0))));\n\n  // local monitor\n  // =============\n\n  if ((opts & Options::OPT_DISABLE_LOCAL_MONITOR) == 0) {\n\n    /* Rule #27: Clear local monitor state\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::ARM::t2CLREX),\n        conv_unique<PatchGenerator>(\n            ItPatch::unique(false),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable)))));\n\n    /* Rule #28: Clear local monitor state on svc\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::ARM::tSVC),\n        conv_unique<PatchGenerator>(\n            ItPatch::unique(false),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            // for SVC, we need to backup the value of Temp(0) after the syscall\n            SaveTemp::unique(Temp(0), true),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable)))));\n\n    /* Rule #29: exclusive load 1 register\n     */\n    rules.emplace_back(\n        Or::unique(\n            conv_unique<PatchCondition>(OpIs::unique(llvm::ARM::t2LDREXB),\n                                        OpIs::unique(llvm::ARM::t2LDREXH))),\n        conv_unique<PatchGenerator>(\n            GetConstant::unique(Temp(0), Constant(1)),\n            GetConstantMap::unique(Temp(0),\n                                   std::map<unsigned, Constant>({\n                                       {llvm::ARM::t2LDREXB, Constant(1)},\n                                       {llvm::ARM::t2LDREXH, Constant(2)},\n                                   })),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            WriteOperandCC::unique(\n                Operand(1),\n                Offset(offsetof(Context, gprState.localMonitor.addr))),\n            ItPatch::unique(false),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec())));\n\n    /* Rule #30: exclusive load 1 register + offset\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::ARM::t2LDREX),\n        conv_unique<PatchGenerator>(\n            GetConstant::unique(Temp(0), Constant(4)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            AddOperandToTemp::unique(Temp(0), Operand(1), Operand(2)),\n            WriteTempCC::unique(\n                Temp(0), Offset(offsetof(Context, gprState.localMonitor.addr))),\n            ItPatch::unique(false),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec())));\n\n    /* Rule #31: exclusive load 2 registers\n     */\n    rules.emplace_back(\n        OpIs::unique(llvm::ARM::t2LDREXD),\n        conv_unique<PatchGenerator>(\n            GetConstant::unique(Temp(0), Constant(8)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable))),\n            WriteOperandCC::unique(\n                Operand(2),\n                Offset(offsetof(Context, gprState.localMonitor.addr))),\n            ItPatch::unique(false),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec())));\n\n    /* Rule #32: exclusive store\n     */\n    rules.emplace_back(\n        Or::unique(conv_unique<PatchCondition>(\n            OpIs::unique(llvm::ARM::t2STREX), OpIs::unique(llvm::ARM::t2STREXB),\n            OpIs::unique(llvm::ARM::t2STREXD),\n            OpIs::unique(llvm::ARM::t2STREXH))),\n        conv_unique<PatchGenerator>(\n            CondExclusifLoad::unique(Temp(0), Temp(1)), ItPatch::unique(false),\n            ModifyInstruction::unique(InstTransform::UniquePtrVec()),\n            GetConstant::unique(Temp(0), Constant(0)),\n            WriteTempCC::unique(\n                Temp(0),\n                Offset(offsetof(Context, gprState.localMonitor.enable)))));\n  }\n\n  // Instruction with no PC\n  // ======================\n\n  /* Rule #33: instruction to skip (it, barrier, preload)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::ARM::t2PLDWi12), OpIs::unique(llvm::ARM::t2PLDWi8),\n          OpIs::unique(llvm::ARM::t2PLDWs), OpIs::unique(llvm::ARM::t2PLDi12),\n          OpIs::unique(llvm::ARM::t2PLDi8), OpIs::unique(llvm::ARM::t2PLDpci),\n          OpIs::unique(llvm::ARM::t2PLDs), OpIs::unique(llvm::ARM::t2PLIi12),\n          OpIs::unique(llvm::ARM::t2PLIi8), OpIs::unique(llvm::ARM::t2PLIpci),\n          OpIs::unique(llvm::ARM::t2PLIs), OpIs::unique(llvm::ARM::t2IT)\n\n              )),\n      PatchGenerator::UniquePtrVec());\n\n  /* Rule #34: all other\n   */\n  rules.emplace_back(\n      Not::unique(Or::unique(conv_unique<PatchCondition>(\n          UseReg::unique(Reg(REG_PC)),\n          // unsupported instruction\n          OpIs::unique(llvm::ARM::tSETEND), OpIs::unique(llvm::ARM::t2BXJ),\n          // operand invalid with the current ITBlock\n          OpIs::unique(llvm::ARM::t2B), OpIs::unique(llvm::ARM::t2BXAUT),\n          OpIs::unique(llvm::ARM::t2Bcc), OpIs::unique(llvm::ARM::t2TBB),\n          OpIs::unique(llvm::ARM::t2TBH), OpIs::unique(llvm::ARM::tB),\n          OpIs::unique(llvm::ARM::tBL), OpIs::unique(llvm::ARM::tBLXi),\n          OpIs::unique(llvm::ARM::tBLXr), OpIs::unique(llvm::ARM::tBX),\n          OpIs::unique(llvm::ARM::tBcc), OpIs::unique(llvm::ARM::tCBNZ),\n          OpIs::unique(llvm::ARM::tCBZ), OpIs::unique(llvm::ARM::tLDRpci)\n\n              ))),\n      conv_unique<PatchGenerator>(\n          ItPatch::unique(false),\n          ModifyInstruction::unique(InstTransform::UniquePtrVec())));\n\n  return rules;\n}\n\n} // namespace\n\nPatchRuleAssembly::PatchRuleAssembly(Options opts)\n    : patchRulesARM(getARMPatchRules(opts)),\n      patchRulesThumb(getThumbPatchRules(opts)), options(opts),\n      itRemainingInst(0), itCond({0}) {}\n\nPatchRuleAssembly::~PatchRuleAssembly() = default;\n\nvoid PatchRuleAssembly::reset() { itRemainingInst = 0; }\n\nbool PatchRuleAssembly::changeOptions(Options opts) {\n  // reset the current state. Options cannot be change during the Engine::patch\n  // method\n  reset();\n\n  const Options needRecreate =\n      Options::OPT_DISABLE_FPR | Options::OPT_DISABLE_OPTIONAL_FPR |\n      Options::OPT_DISABLE_D16_D31 | Options::OPT_ARM_MASK |\n      Options::OPT_DISABLE_MEMORYACCESS_VALUE;\n  if ((opts & needRecreate) != (options & needRecreate)) {\n    reset();\n    patchRulesARM = getARMPatchRules(opts);\n    patchRulesThumb = getThumbPatchRules(opts);\n    options = opts;\n    return true;\n  }\n  options = opts;\n  return false;\n}\n\nvoid PatchRuleAssembly::patchSTLDMARM(Patch &patch, const LLVMCPU &llvmcpu) {\n  // need a least 3 tmpRegister and one SR register\n  const unsigned TempRegisterMinimum = 4;\n\n  unsigned usedRegister = 0;\n  for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n    if (patch.regUsage[i] != 0 and\n        (patch.regUsage[i] & RegisterUsage::RegisterBoth) != 0) {\n      usedRegister++;\n    }\n  }\n\n  if (usedRegister + TempRegisterMinimum <= AVAILABLE_GPR) {\n    // the instruction has enought TempRegister\n    return;\n  }\n\n  QBDI_REQUIRE_ABORT(0 < patch.metadata.inst.getNumOperands(),\n                     \"Invalid instruction {}\", patch);\n  QBDI_REQUIRE_ABORT(patch.metadata.inst.getOperand(0).isReg(),\n                     \"Unexpected operand type {}\", patch);\n\n  // don't used the base address as a tempregister\n  RegLLVM baseReg = patch.metadata.inst.getOperand(0).getReg();\n  bool needSR = (llvmcpu == CPUMode::Thumb);\n\n  if (needSR) {\n    // try to set RegisterSavedScratch on a unused register\n    for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n      if (patch.regUsage[i] == 0 and GPR_ID[i] != baseReg) {\n        patch.regUsage[i] |= RegisterUsage::RegisterSavedScratch;\n        needSR = false;\n        break;\n      }\n    }\n  }\n\n  for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n    if (patch.regUsage[i] != 0 and\n        (patch.regUsage[i] & RegisterUsage::RegisterSavedScratch) == 0 and\n        GPR_ID[i] != baseReg) {\n      if (needSR) {\n        patch.regUsage[i] |= RegisterUsage::RegisterSavedScratch;\n        needSR = false;\n      } else {\n        patch.regUsage[i] |= RegisterUsage::RegisterSaved;\n      }\n    }\n  }\n\n  return;\n}\n\nbool PatchRuleAssembly::generate(const llvm::MCInst &inst, rword address,\n                                 uint32_t instSize, const LLVMCPU &llvmcpu,\n                                 std::vector<Patch> &patchList) {\n  if (llvmcpu.getCPUMode() == CPUMode::ARM) {\n    return generateARM(inst, address, instSize, llvmcpu, patchList);\n  } else {\n    return generateThumb(inst, address, instSize, llvmcpu, patchList);\n  }\n}\n\nbool PatchRuleAssembly::generateARM(const llvm::MCInst &inst, rword address,\n                                    uint32_t instSize, const LLVMCPU &llvmcpu,\n                                    std::vector<Patch> &patchList) {\n\n  QBDI_REQUIRE_ABORT(itRemainingInst == 0, \"Unexpected state\");\n  Patch instPatch{inst, address, instSize, llvmcpu};\n  instPatch.metadata.archMetadata.cond = getCondition(inst, llvmcpu);\n  instPatch.metadata.archMetadata.posITblock = 0;\n\n  switch (inst.getOpcode()) {\n    case llvm::ARM::LDMIA:\n    case llvm::ARM::LDMIB:\n    case llvm::ARM::LDMDA:\n    case llvm::ARM::LDMDB:\n    case llvm::ARM::LDMIA_UPD:\n    case llvm::ARM::LDMIB_UPD:\n    case llvm::ARM::LDMDA_UPD:\n    case llvm::ARM::LDMDB_UPD:\n    case llvm::ARM::STMIA:\n    case llvm::ARM::STMIB:\n    case llvm::ARM::STMDA:\n    case llvm::ARM::STMDB:\n    case llvm::ARM::STMIA_UPD:\n    case llvm::ARM::STMIB_UPD:\n    case llvm::ARM::STMDA_UPD:\n    case llvm::ARM::STMDB_UPD:\n      // LDM and STM can use all register\n      // mark some register available for the temp register\n      patchSTLDMARM(instPatch, llvmcpu);\n      break;\n    default:\n      break;\n  }\n\n  for (uint32_t j = 0; j < patchRulesARM.size(); j++) {\n    if (patchRulesARM[j].canBeApplied(instPatch, llvmcpu)) {\n      QBDI_DEBUG(\"Patch ARM rule {} applied\", j);\n\n      patchRulesARM[j].apply(instPatch, llvmcpu);\n      patchList.push_back(std::move(instPatch));\n      Patch &patch = patchList.back();\n\n      if (patch.metadata.modifyPC) {\n        reset();\n        return true;\n      } else {\n        return false;\n      }\n    }\n  }\n  QBDI_ABORT(\"Not PatchRule found {}\", instPatch);\n}\n\nbool PatchRuleAssembly::generateThumb(const llvm::MCInst &inst, rword address,\n                                      uint32_t instSize, const LLVMCPU &llvmcpu,\n                                      std::vector<Patch> &patchList) {\n\n  Patch instPatch{inst, address, instSize, llvmcpu};\n  instPatch.metadata.archMetadata.cond = getCondition(inst, llvmcpu);\n  instPatch.metadata.archMetadata.posITblock = itRemainingInst;\n\n  switch (inst.getOpcode()) {\n    case llvm::ARM::t2LDMIA:\n    case llvm::ARM::t2LDMDB:\n    case llvm::ARM::t2LDMIA_UPD:\n    case llvm::ARM::t2LDMDB_UPD:\n    case llvm::ARM::t2STMIA:\n    case llvm::ARM::t2STMDB:\n    case llvm::ARM::t2STMIA_UPD:\n    case llvm::ARM::t2STMDB_UPD:\n      // LDM and STM can use all register\n      // mark some register available for the temp register\n      patchSTLDMARM(instPatch, llvmcpu);\n      break;\n    default:\n      break;\n  }\n\n  if (inst.getOpcode() == llvm::ARM::t2IT) {\n    QBDI_REQUIRE_ABORT(itRemainingInst == 0,\n                       \"IT instruction cannot be inside another IT block {}\",\n                       instPatch);\n    QBDI_REQUIRE_ABORT(2 == inst.getNumOperands(), \"Invalid instruction {}\",\n                       instPatch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(0).isImm(), \"Unexpected operand type {}\",\n                       instPatch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(1).isImm(), \"Unexpected operand type {}\",\n                       instPatch);\n    unsigned mask = inst.getOperand(1).getImm();\n    switch (mask) {\n      case (unsigned)llvm::ARM::PredBlockMask::T:\n        itRemainingInst = 1;\n        break;\n      case (unsigned)llvm::ARM::PredBlockMask::TE:\n      case (unsigned)llvm::ARM::PredBlockMask::TT:\n        itRemainingInst = 2;\n        break;\n      case (unsigned)llvm::ARM::PredBlockMask::TTT:\n      case (unsigned)llvm::ARM::PredBlockMask::TTE:\n      case (unsigned)llvm::ARM::PredBlockMask::TET:\n      case (unsigned)llvm::ARM::PredBlockMask::TEE:\n        itRemainingInst = 3;\n        break;\n      case (unsigned)llvm::ARM::PredBlockMask::TTTT:\n      case (unsigned)llvm::ARM::PredBlockMask::TTTE:\n      case (unsigned)llvm::ARM::PredBlockMask::TTET:\n      case (unsigned)llvm::ARM::PredBlockMask::TTEE:\n      case (unsigned)llvm::ARM::PredBlockMask::TETT:\n      case (unsigned)llvm::ARM::PredBlockMask::TETE:\n      case (unsigned)llvm::ARM::PredBlockMask::TEET:\n      case (unsigned)llvm::ARM::PredBlockMask::TEEE:\n        itRemainingInst = 4;\n        break;\n      default:\n        QBDI_ABORT(\"Unexpected IT mask {} {}\", mask, instPatch);\n    }\n    itCond[0] = inst.getOperand(0).getImm();\n    for (unsigned i = 1; i < itRemainingInst; i++) {\n      if ((mask & (1 << (4 - i))) == 0) {\n        itCond[i] = itCond[0];\n      } else {\n        itCond[i] = llvm::ARMCC::getOppositeCondition(\n            (llvm::ARMCC::CondCodes)itCond[0]);\n      }\n    }\n  } else if (itRemainingInst > 0) {\n    QBDI_REQUIRE_ABORT(instPatch.metadata.archMetadata.cond == itCond[0],\n                       \"Condition doesn't match the last IT condition {}\",\n                       instPatch);\n    itRemainingInst--;\n    itCond = {itCond[1], itCond[2], itCond[3], 0};\n  }\n\n  for (uint32_t j = 0; j < patchRulesThumb.size(); j++) {\n    if (patchRulesThumb[j].canBeApplied(instPatch, llvmcpu)) {\n      QBDI_DEBUG(\"Patch Thumb rule {} applied\", j);\n\n      patchRulesThumb[j].apply(instPatch, llvmcpu);\n      patchList.push_back(std::move(instPatch));\n      Patch &patch = patchList.back();\n\n      if (patch.metadata.modifyPC) {\n        QBDI_REQUIRE_ABORT(itRemainingInst == 0,\n                           \"Modify PC before the end of ItBlock {}\", instPatch);\n        reset();\n        return true;\n      } else {\n        return false;\n      }\n    }\n  }\n  QBDI_ABORT(\"Not PatchRule found {}\", instPatch);\n}\n\nbool PatchRuleAssembly::earlyEnd(const LLVMCPU &llvmcpu,\n                                 std::vector<Patch> &patchList) {\n  reset();\n  return true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/PatchRuleAssembly_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHRULEASSEMBLY_ARM_H\n#define PATCHRULEASSEMBLY_ARM_H\n\n#include <array>\n#include \"Patch/PatchRuleAssemblyBase.h\"\n\nnamespace QBDI {\nclass PatchRule;\n\nclass PatchRuleAssembly final : public PatchRuleAssemblyBase {\n  std::vector<PatchRule> patchRulesARM;\n  std::vector<PatchRule> patchRulesThumb;\n  Options options;\n  unsigned itRemainingInst;\n  std::array<uint8_t, 4> itCond;\n\n  void reset();\n\n  // ARM\n  bool generateARM(const llvm::MCInst &inst, rword address, uint32_t instSize,\n                   const LLVMCPU &llvmcpu, std::vector<Patch> &patchList);\n\n  void patchSTLDMARM(Patch &patch, const LLVMCPU &llvmcpu);\n\n  // Thumb\n  bool generateThumb(const llvm::MCInst &inst, rword address, uint32_t instSize,\n                     const LLVMCPU &llvmcpu, std::vector<Patch> &patchList);\n\npublic:\n  PatchRuleAssembly(Options opts);\n\n  ~PatchRuleAssembly();\n\n  bool changeOptions(Options opts) override;\n\n  bool generate(const llvm::MCInst &inst, rword address, uint32_t instSize,\n                const LLVMCPU &llvmcpu, std::vector<Patch> &patchList) override;\n\n  bool earlyEnd(const LLVMCPU &llvmcpu, std::vector<Patch> &patchList) override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/Register_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ARMInstrInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/ARM/InstInfo_ARM.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nstatic_assert(sizeof(FPRStateVReg) == QBDI_NUM_FPR * 8,\n              \"Wrong size for FPRState\");\n\nconst RegLLVM GPR_ID[] = {llvm::ARM::R0,  llvm::ARM::R1,  llvm::ARM::R2,\n                          llvm::ARM::R3,  llvm::ARM::R4,  llvm::ARM::R5,\n                          llvm::ARM::R6,  llvm::ARM::R7,  llvm::ARM::R8,\n                          llvm::ARM::R9,  llvm::ARM::R10, llvm::ARM::R11,\n                          llvm::ARM::R12, llvm::ARM::SP,  llvm::ARM::LR,\n                          llvm::ARM::PC,  llvm::ARM::CPSR};\n\nconst RegLLVM FLAG_ID[] = {};\n\nconst RegLLVM SEG_ID[] = {llvm::ARM::ITSTATE};\n\nconst std::map<RegLLVM, int16_t> FPR_ID = {\n    // size 4b\n    {llvm::ARM::S0, offsetof(FPRState, vreg.s[0])},\n    {llvm::ARM::S1, offsetof(FPRState, vreg.s[1])},\n    {llvm::ARM::S2, offsetof(FPRState, vreg.s[2])},\n    {llvm::ARM::S3, offsetof(FPRState, vreg.s[3])},\n    {llvm::ARM::S4, offsetof(FPRState, vreg.s[4])},\n    {llvm::ARM::S5, offsetof(FPRState, vreg.s[5])},\n    {llvm::ARM::S6, offsetof(FPRState, vreg.s[6])},\n    {llvm::ARM::S7, offsetof(FPRState, vreg.s[7])},\n    {llvm::ARM::S8, offsetof(FPRState, vreg.s[8])},\n    {llvm::ARM::S9, offsetof(FPRState, vreg.s[9])},\n    {llvm::ARM::S10, offsetof(FPRState, vreg.s[10])},\n    {llvm::ARM::S11, offsetof(FPRState, vreg.s[11])},\n    {llvm::ARM::S12, offsetof(FPRState, vreg.s[12])},\n    {llvm::ARM::S13, offsetof(FPRState, vreg.s[13])},\n    {llvm::ARM::S14, offsetof(FPRState, vreg.s[14])},\n    {llvm::ARM::S15, offsetof(FPRState, vreg.s[15])},\n    {llvm::ARM::S16, offsetof(FPRState, vreg.s[16])},\n    {llvm::ARM::S17, offsetof(FPRState, vreg.s[17])},\n    {llvm::ARM::S18, offsetof(FPRState, vreg.s[18])},\n    {llvm::ARM::S19, offsetof(FPRState, vreg.s[19])},\n    {llvm::ARM::S20, offsetof(FPRState, vreg.s[20])},\n    {llvm::ARM::S21, offsetof(FPRState, vreg.s[21])},\n    {llvm::ARM::S22, offsetof(FPRState, vreg.s[22])},\n    {llvm::ARM::S23, offsetof(FPRState, vreg.s[23])},\n    {llvm::ARM::S24, offsetof(FPRState, vreg.s[24])},\n    {llvm::ARM::S25, offsetof(FPRState, vreg.s[25])},\n    {llvm::ARM::S26, offsetof(FPRState, vreg.s[26])},\n    {llvm::ARM::S27, offsetof(FPRState, vreg.s[27])},\n    {llvm::ARM::S28, offsetof(FPRState, vreg.s[28])},\n    {llvm::ARM::S29, offsetof(FPRState, vreg.s[29])},\n    {llvm::ARM::S30, offsetof(FPRState, vreg.s[30])},\n    {llvm::ARM::S31, offsetof(FPRState, vreg.s[31])},\n\n    // size 8b\n    {llvm::ARM::D0, offsetof(FPRState, vreg.d[0])},\n    {llvm::ARM::D1, offsetof(FPRState, vreg.d[1])},\n    {llvm::ARM::D2, offsetof(FPRState, vreg.d[2])},\n    {llvm::ARM::D3, offsetof(FPRState, vreg.d[3])},\n    {llvm::ARM::D4, offsetof(FPRState, vreg.d[4])},\n    {llvm::ARM::D5, offsetof(FPRState, vreg.d[5])},\n    {llvm::ARM::D6, offsetof(FPRState, vreg.d[6])},\n    {llvm::ARM::D7, offsetof(FPRState, vreg.d[7])},\n    {llvm::ARM::D8, offsetof(FPRState, vreg.d[8])},\n    {llvm::ARM::D9, offsetof(FPRState, vreg.d[9])},\n    {llvm::ARM::D10, offsetof(FPRState, vreg.d[10])},\n    {llvm::ARM::D11, offsetof(FPRState, vreg.d[11])},\n    {llvm::ARM::D12, offsetof(FPRState, vreg.d[12])},\n    {llvm::ARM::D13, offsetof(FPRState, vreg.d[13])},\n    {llvm::ARM::D14, offsetof(FPRState, vreg.d[14])},\n    {llvm::ARM::D15, offsetof(FPRState, vreg.d[15])},\n#if QBDI_NUM_FPR == 32\n    {llvm::ARM::D16, offsetof(FPRState, vreg.d[16])},\n    {llvm::ARM::D17, offsetof(FPRState, vreg.d[17])},\n    {llvm::ARM::D18, offsetof(FPRState, vreg.d[18])},\n    {llvm::ARM::D19, offsetof(FPRState, vreg.d[19])},\n    {llvm::ARM::D20, offsetof(FPRState, vreg.d[20])},\n    {llvm::ARM::D21, offsetof(FPRState, vreg.d[21])},\n    {llvm::ARM::D22, offsetof(FPRState, vreg.d[22])},\n    {llvm::ARM::D23, offsetof(FPRState, vreg.d[23])},\n    {llvm::ARM::D24, offsetof(FPRState, vreg.d[24])},\n    {llvm::ARM::D25, offsetof(FPRState, vreg.d[25])},\n    {llvm::ARM::D26, offsetof(FPRState, vreg.d[26])},\n    {llvm::ARM::D27, offsetof(FPRState, vreg.d[27])},\n    {llvm::ARM::D28, offsetof(FPRState, vreg.d[28])},\n    {llvm::ARM::D29, offsetof(FPRState, vreg.d[29])},\n    {llvm::ARM::D30, offsetof(FPRState, vreg.d[30])},\n    {llvm::ARM::D31, offsetof(FPRState, vreg.d[31])},\n#else\n    {llvm::ARM::D16, -1},\n    {llvm::ARM::D17, -1},\n    {llvm::ARM::D18, -1},\n    {llvm::ARM::D19, -1},\n    {llvm::ARM::D20, -1},\n    {llvm::ARM::D21, -1},\n    {llvm::ARM::D22, -1},\n    {llvm::ARM::D23, -1},\n    {llvm::ARM::D24, -1},\n    {llvm::ARM::D25, -1},\n    {llvm::ARM::D26, -1},\n    {llvm::ARM::D27, -1},\n    {llvm::ARM::D28, -1},\n    {llvm::ARM::D29, -1},\n    {llvm::ARM::D30, -1},\n    {llvm::ARM::D31, -1},\n#endif\n\n    // size 16b\n    {llvm::ARM::Q0, offsetof(FPRState, vreg.q[0])},\n    {llvm::ARM::Q1, offsetof(FPRState, vreg.q[1])},\n    {llvm::ARM::Q2, offsetof(FPRState, vreg.q[2])},\n    {llvm::ARM::Q3, offsetof(FPRState, vreg.q[3])},\n    {llvm::ARM::Q4, offsetof(FPRState, vreg.q[4])},\n    {llvm::ARM::Q5, offsetof(FPRState, vreg.q[5])},\n    {llvm::ARM::Q6, offsetof(FPRState, vreg.q[6])},\n    {llvm::ARM::Q7, offsetof(FPRState, vreg.q[7])},\n#if QBDI_NUM_FPR == 32\n    {llvm::ARM::Q8, offsetof(FPRState, vreg.q[8])},\n    {llvm::ARM::Q9, offsetof(FPRState, vreg.q[9])},\n    {llvm::ARM::Q10, offsetof(FPRState, vreg.q[10])},\n    {llvm::ARM::Q11, offsetof(FPRState, vreg.q[11])},\n    {llvm::ARM::Q12, offsetof(FPRState, vreg.q[12])},\n    {llvm::ARM::Q13, offsetof(FPRState, vreg.q[13])},\n    {llvm::ARM::Q14, offsetof(FPRState, vreg.q[14])},\n    {llvm::ARM::Q15, offsetof(FPRState, vreg.q[15])},\n#else\n    {llvm::ARM::Q8, -1},\n    {llvm::ARM::Q9, -1},\n    {llvm::ARM::Q10, -1},\n    {llvm::ARM::Q11, -1},\n    {llvm::ARM::Q12, -1},\n    {llvm::ARM::Q13, -1},\n    {llvm::ARM::Q14, -1},\n    {llvm::ARM::Q15, -1},\n#endif\n};\n\nconst unsigned int size_GPR_ID = sizeof(GPR_ID) / sizeof(RegLLVM);\nconst unsigned int size_FLAG_ID = sizeof(FLAG_ID) / sizeof(RegLLVM);\nconst unsigned int size_SEG_ID = sizeof(SEG_ID) / sizeof(RegLLVM);\n\nnamespace {\n\nconstexpr uint16_t REGISTER_4BYTES[] = {\n    llvm::ARM::R0,  llvm::ARM::R1,  llvm::ARM::R2,  llvm::ARM::R3,\n    llvm::ARM::R4,  llvm::ARM::R5,  llvm::ARM::R6,  llvm::ARM::R7,\n    llvm::ARM::R8,  llvm::ARM::R9,  llvm::ARM::R10, llvm::ARM::R11,\n    llvm::ARM::R12, llvm::ARM::SP,  llvm::ARM::LR,  llvm::ARM::PC,\n\n    llvm::ARM::S0,  llvm::ARM::S1,  llvm::ARM::S2,  llvm::ARM::S3,\n    llvm::ARM::S4,  llvm::ARM::S5,  llvm::ARM::S6,  llvm::ARM::S7,\n    llvm::ARM::S8,  llvm::ARM::S9,  llvm::ARM::S10, llvm::ARM::S11,\n    llvm::ARM::S12, llvm::ARM::S13, llvm::ARM::S14, llvm::ARM::S15,\n    llvm::ARM::S16, llvm::ARM::S17, llvm::ARM::S18, llvm::ARM::S19,\n    llvm::ARM::S20, llvm::ARM::S21, llvm::ARM::S22, llvm::ARM::S23,\n    llvm::ARM::S24, llvm::ARM::S25, llvm::ARM::S26, llvm::ARM::S27,\n    llvm::ARM::S28, llvm::ARM::S29, llvm::ARM::S30, llvm::ARM::S31,\n};\n\nconstexpr size_t REGISTER_4BYTES_SIZE =\n    sizeof(REGISTER_4BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_4BYTES_P2[] = {\n    llvm::ARM::R0_R1, llvm::ARM::R2_R3,   llvm::ARM::R4_R5,  llvm::ARM::R6_R7,\n    llvm::ARM::R8_R9, llvm::ARM::R10_R11, llvm::ARM::R12_SP,\n};\n\nconstexpr size_t REGISTER_4BYTES_P2_SIZE =\n    sizeof(REGISTER_4BYTES_P2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES[] = {\n    llvm::ARM::D0,  llvm::ARM::D1,  llvm::ARM::D2,  llvm::ARM::D3,\n    llvm::ARM::D4,  llvm::ARM::D5,  llvm::ARM::D6,  llvm::ARM::D7,\n    llvm::ARM::D8,  llvm::ARM::D9,  llvm::ARM::D10, llvm::ARM::D11,\n    llvm::ARM::D12, llvm::ARM::D13, llvm::ARM::D14, llvm::ARM::D15,\n    llvm::ARM::D16, llvm::ARM::D17, llvm::ARM::D18, llvm::ARM::D19,\n    llvm::ARM::D20, llvm::ARM::D21, llvm::ARM::D22, llvm::ARM::D23,\n    llvm::ARM::D24, llvm::ARM::D25, llvm::ARM::D26, llvm::ARM::D27,\n    llvm::ARM::D28, llvm::ARM::D29, llvm::ARM::D30, llvm::ARM::D31,\n};\n\nconstexpr size_t REGISTER_8BYTES_SIZE =\n    sizeof(REGISTER_8BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P2[] = {\n    llvm::ARM::D1_D2,   llvm::ARM::D3_D4,   llvm::ARM::D5_D6,\n    llvm::ARM::D7_D8,   llvm::ARM::D9_D10,  llvm::ARM::D11_D12,\n    llvm::ARM::D13_D14, llvm::ARM::D15_D16, llvm::ARM::D17_D18,\n    llvm::ARM::D19_D20, llvm::ARM::D21_D22, llvm::ARM::D23_D24,\n    llvm::ARM::D25_D26, llvm::ARM::D27_D28, llvm::ARM::D29_D30,\n};\n\nconstexpr size_t REGISTER_8BYTES_P2_SIZE =\n    sizeof(REGISTER_8BYTES_P2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P2_S2[] = {\n    llvm::ARM::D0_D2,   llvm::ARM::D1_D3,   llvm::ARM::D2_D4,\n    llvm::ARM::D3_D5,   llvm::ARM::D4_D6,   llvm::ARM::D5_D7,\n    llvm::ARM::D6_D8,   llvm::ARM::D7_D9,   llvm::ARM::D8_D10,\n    llvm::ARM::D9_D11,  llvm::ARM::D10_D12, llvm::ARM::D11_D13,\n    llvm::ARM::D12_D14, llvm::ARM::D13_D15, llvm::ARM::D14_D16,\n    llvm::ARM::D15_D17, llvm::ARM::D16_D18, llvm::ARM::D17_D19,\n    llvm::ARM::D18_D20, llvm::ARM::D19_D21, llvm::ARM::D20_D22,\n    llvm::ARM::D21_D23, llvm::ARM::D22_D24, llvm::ARM::D23_D25,\n    llvm::ARM::D24_D26, llvm::ARM::D25_D27, llvm::ARM::D26_D28,\n    llvm::ARM::D27_D29, llvm::ARM::D28_D30, llvm::ARM::D29_D31,\n};\n\nconstexpr size_t REGISTER_8BYTES_P2_S2_SIZE =\n    sizeof(REGISTER_8BYTES_P2_S2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P3[] = {\n    llvm::ARM::D0_D1_D2,    llvm::ARM::D1_D2_D3,    llvm::ARM::D2_D3_D4,\n    llvm::ARM::D3_D4_D5,    llvm::ARM::D4_D5_D6,    llvm::ARM::D5_D6_D7,\n    llvm::ARM::D6_D7_D8,    llvm::ARM::D7_D8_D9,    llvm::ARM::D8_D9_D10,\n    llvm::ARM::D9_D10_D11,  llvm::ARM::D10_D11_D12, llvm::ARM::D11_D12_D13,\n    llvm::ARM::D12_D13_D14, llvm::ARM::D13_D14_D15, llvm::ARM::D14_D15_D16,\n    llvm::ARM::D15_D16_D17, llvm::ARM::D16_D17_D18, llvm::ARM::D17_D18_D19,\n    llvm::ARM::D18_D19_D20, llvm::ARM::D19_D20_D21, llvm::ARM::D20_D21_D22,\n    llvm::ARM::D21_D22_D23, llvm::ARM::D22_D23_D24, llvm::ARM::D23_D24_D25,\n    llvm::ARM::D24_D25_D26, llvm::ARM::D25_D26_D27, llvm::ARM::D26_D27_D28,\n    llvm::ARM::D27_D28_D29, llvm::ARM::D28_D29_D30, llvm::ARM::D29_D30_D31,\n};\n\nconstexpr size_t REGISTER_8BYTES_P3_SIZE =\n    sizeof(REGISTER_8BYTES_P3) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P3_S2[] = {\n    llvm::ARM::D0_D2_D4,    llvm::ARM::D1_D3_D5,    llvm::ARM::D2_D4_D6,\n    llvm::ARM::D3_D5_D7,    llvm::ARM::D4_D6_D8,    llvm::ARM::D5_D7_D9,\n    llvm::ARM::D6_D8_D10,   llvm::ARM::D7_D9_D11,   llvm::ARM::D8_D10_D12,\n    llvm::ARM::D9_D11_D13,  llvm::ARM::D10_D12_D14, llvm::ARM::D11_D13_D15,\n    llvm::ARM::D12_D14_D16, llvm::ARM::D13_D15_D17, llvm::ARM::D14_D16_D18,\n    llvm::ARM::D15_D17_D19, llvm::ARM::D16_D18_D20, llvm::ARM::D17_D19_D21,\n    llvm::ARM::D18_D20_D22, llvm::ARM::D19_D21_D23, llvm::ARM::D20_D22_D24,\n    llvm::ARM::D21_D23_D25, llvm::ARM::D22_D24_D26, llvm::ARM::D23_D25_D27,\n    llvm::ARM::D24_D26_D28, llvm::ARM::D25_D27_D29, llvm::ARM::D26_D28_D30,\n    llvm::ARM::D27_D29_D31,\n};\n\nconstexpr size_t REGISTER_8BYTES_P3_S2_SIZE =\n    sizeof(REGISTER_8BYTES_P3_S2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P4[] = {\n    llvm::ARM::D1_D2_D3_D4,     llvm::ARM::D3_D4_D5_D6,\n    llvm::ARM::D5_D6_D7_D8,     llvm::ARM::D7_D8_D9_D10,\n    llvm::ARM::D9_D10_D11_D12,  llvm::ARM::D11_D12_D13_D14,\n    llvm::ARM::D13_D14_D15_D16, llvm::ARM::D15_D16_D17_D18,\n    llvm::ARM::D17_D18_D19_D20, llvm::ARM::D19_D20_D21_D22,\n    llvm::ARM::D21_D22_D23_D24, llvm::ARM::D23_D24_D25_D26,\n    llvm::ARM::D25_D26_D27_D28, llvm::ARM::D27_D28_D29_D30,\n};\n\nconstexpr size_t REGISTER_8BYTES_P4_SIZE =\n    sizeof(REGISTER_8BYTES_P4) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES_P4_S2[] = {\n    llvm::ARM::D0_D2_D4_D6,     llvm::ARM::D1_D3_D5_D7,\n    llvm::ARM::D2_D4_D6_D8,     llvm::ARM::D3_D5_D7_D9,\n    llvm::ARM::D4_D6_D8_D10,    llvm::ARM::D5_D7_D9_D11,\n    llvm::ARM::D6_D8_D10_D12,   llvm::ARM::D7_D9_D11_D13,\n    llvm::ARM::D8_D10_D12_D14,  llvm::ARM::D9_D11_D13_D15,\n    llvm::ARM::D10_D12_D14_D16, llvm::ARM::D11_D13_D15_D17,\n    llvm::ARM::D12_D14_D16_D18, llvm::ARM::D13_D15_D17_D19,\n    llvm::ARM::D14_D16_D18_D20, llvm::ARM::D15_D17_D19_D21,\n    llvm::ARM::D16_D18_D20_D22, llvm::ARM::D17_D19_D21_D23,\n    llvm::ARM::D18_D20_D22_D24, llvm::ARM::D19_D21_D23_D25,\n    llvm::ARM::D20_D22_D24_D26, llvm::ARM::D21_D23_D25_D27,\n    llvm::ARM::D22_D24_D26_D28, llvm::ARM::D23_D25_D27_D29,\n    llvm::ARM::D24_D26_D28_D30, llvm::ARM::D25_D27_D29_D31,\n};\n\nconstexpr size_t REGISTER_8BYTES_P4_S2_SIZE =\n    sizeof(REGISTER_8BYTES_P4_S2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES[] = {\n    llvm::ARM::Q0,  llvm::ARM::Q1,  llvm::ARM::Q2,  llvm::ARM::Q3,\n    llvm::ARM::Q4,  llvm::ARM::Q5,  llvm::ARM::Q6,  llvm::ARM::Q7,\n    llvm::ARM::Q8,  llvm::ARM::Q9,  llvm::ARM::Q10, llvm::ARM::Q11,\n    llvm::ARM::Q12, llvm::ARM::Q13, llvm::ARM::Q14, llvm::ARM::Q15,\n};\n\nconstexpr size_t REGISTER_16BYTES_SIZE =\n    sizeof(REGISTER_16BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES_P2[] = {\n    llvm::ARM::Q0_Q1,   llvm::ARM::Q1_Q2,   llvm::ARM::Q2_Q3,\n    llvm::ARM::Q3_Q4,   llvm::ARM::Q4_Q5,   llvm::ARM::Q5_Q6,\n    llvm::ARM::Q6_Q7,   llvm::ARM::Q7_Q8,   llvm::ARM::Q8_Q9,\n    llvm::ARM::Q9_Q10,  llvm::ARM::Q10_Q11, llvm::ARM::Q11_Q12,\n    llvm::ARM::Q12_Q13, llvm::ARM::Q13_Q14, llvm::ARM::Q14_Q15,\n};\n\nconstexpr size_t REGISTER_16BYTES_P2_SIZE =\n    sizeof(REGISTER_16BYTES_P2) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES_P4[] = {\n    llvm::ARM::Q0_Q1_Q2_Q3,     llvm::ARM::Q1_Q2_Q3_Q4,\n    llvm::ARM::Q2_Q3_Q4_Q5,     llvm::ARM::Q3_Q4_Q5_Q6,\n    llvm::ARM::Q4_Q5_Q6_Q7,     llvm::ARM::Q5_Q6_Q7_Q8,\n    llvm::ARM::Q6_Q7_Q8_Q9,     llvm::ARM::Q7_Q8_Q9_Q10,\n    llvm::ARM::Q8_Q9_Q10_Q11,   llvm::ARM::Q9_Q10_Q11_Q12,\n    llvm::ARM::Q10_Q11_Q12_Q13, llvm::ARM::Q11_Q12_Q13_Q14,\n    llvm::ARM::Q12_Q13_Q14_Q15,\n};\n\nconstexpr size_t REGISTER_16BYTES_P4_SIZE =\n    sizeof(REGISTER_16BYTES_P4) / sizeof(uint16_t);\n\n/* Encode base register on one byte\n * -1: invalid\n *  0 : R0 ... 12 : R12\n * 13 : SP\n * 14 : LR\n * 15 : PC\n * 16:  Q0 ... 31 : Q15\n * 32 : D0 ... 63 : D31\n * 64 : S0 ... 95 : S31\n */\nconstexpr int8_t getEncodedBaseReg(unsigned reg) {\n  // verify llvm register enum\n  static_assert((llvm::ARM::R12 - llvm::ARM::R0) == 12);\n  static_assert((llvm::ARM::S31 - llvm::ARM::S0) == 31);\n  static_assert((llvm::ARM::D31 - llvm::ARM::D0) == 31);\n  static_assert((llvm::ARM::Q15 - llvm::ARM::Q0) == 15);\n  static_assert((llvm::ARM::R12_SP - llvm::ARM::R0_R1) == 6);\n  static_assert((llvm::ARM::D29_D30 - llvm::ARM::D1_D2) == 14);\n  static_assert((llvm::ARM::D29_D31 - llvm::ARM::D0_D2) == 29);\n  static_assert((llvm::ARM::D29_D30_D31 - llvm::ARM::D0_D1_D2) == 29);\n  static_assert((llvm::ARM::D27_D29_D31 - llvm::ARM::D0_D2_D4) == 27);\n  static_assert((llvm::ARM::D27_D28_D29_D30 - llvm::ARM::D1_D2_D3_D4) == 13);\n  static_assert((llvm::ARM::D25_D27_D29_D31 - llvm::ARM::D0_D2_D4_D6) == 25);\n  static_assert((llvm::ARM::Q14_Q15 - llvm::ARM::Q0_Q1) == 14);\n  static_assert((llvm::ARM::Q12_Q13_Q14_Q15 - llvm::ARM::Q0_Q1_Q2_Q3) == 12);\n\n  switch (reg) {\n    case llvm::ARM::SP:\n      return 13;\n    case llvm::ARM::LR:\n      return 14;\n    case llvm::ARM::PC:\n      return 15;\n  }\n  if (llvm::ARM::R0 <= reg and reg <= llvm::ARM::R12) {\n    return reg - llvm::ARM::R0;\n  } else if (llvm::ARM::S0 <= reg and reg <= llvm::ARM::S31) {\n    return 64 + reg - llvm::ARM::S0;\n  } else if (llvm::ARM::D0 <= reg and reg <= llvm::ARM::D31) {\n    return 32 + reg - llvm::ARM::D0;\n  } else if (llvm::ARM::Q0 <= reg and reg <= llvm::ARM::Q15) {\n    return 16 + reg - llvm::ARM::Q0;\n  } else if (llvm::ARM::R0_R1 <= reg and reg <= llvm::ARM::R12_SP) {\n    return (reg - llvm::ARM::R0_R1) * 2;\n  } else if (llvm::ARM::D1_D2 <= reg and reg <= llvm::ARM::D29_D30) {\n    return 33 + (reg - llvm::ARM::D1_D2) * 2;\n  } else if (llvm::ARM::D0_D2 <= reg and reg <= llvm::ARM::D29_D31) {\n    return 32 + (reg - llvm::ARM::D0_D2);\n  } else if (llvm::ARM::D0_D1_D2 <= reg and reg <= llvm::ARM::D29_D30_D31) {\n    return 32 + (reg - llvm::ARM::D0_D1_D2);\n  } else if (llvm::ARM::D0_D2_D4 <= reg and reg <= llvm::ARM::D27_D29_D31) {\n    return 32 + (reg - llvm::ARM::D0_D2_D4);\n  } else if (llvm::ARM::D1_D2_D3_D4 <= reg and\n             reg <= llvm::ARM::D27_D28_D29_D30) {\n    return 33 + (reg - llvm::ARM::D1_D2_D3_D4) * 2;\n  } else if (llvm::ARM::D0_D2_D4_D6 <= reg and\n             reg <= llvm::ARM::D25_D27_D29_D31) {\n    return 32 + (reg - llvm::ARM::D0_D2_D4_D6);\n  } else if (llvm::ARM::Q0_Q1 <= reg and reg <= llvm::ARM::Q14_Q15) {\n    return 16 + (reg - llvm::ARM::Q0_Q1);\n  } else if (llvm::ARM::Q0_Q1_Q2_Q3 <= reg and\n             reg <= llvm::ARM::Q12_Q13_Q14_Q15) {\n    return 16 + (reg - llvm::ARM::Q0_Q1_Q2_Q3);\n  }\n  return -1;\n}\n\nstruct RegisterInfoArray {\n  uint16_t sizeArr[llvm::ARM::NUM_TARGET_REGS] = {0};\n\n  // Use to find the Higher register\n  // -1: invalid\n  //  0 : R0 ... 12 : R12\n  // 13 : SP\n  // 14 : LR\n  // 15 : PC\n  // 16:  Q0 ... 31 : Q15\n  // 32 : D0 ... 63 : D31\n  // 64 : S0 ... 95 : S31\n  int8_t baseReg[llvm::ARM::NUM_TARGET_REGS] = {0};\n\n  constexpr uint16_t setValue(uint8_t size, uint8_t packed, uint8_t spaced) {\n    return (size & 0xff) | ((packed & 0xf) << 8) | ((spaced & 0xf) << 12);\n  }\n\n  constexpr RegisterInfoArray() {\n\n    sizeArr[llvm::ARM::ITSTATE] = setValue(1, 1, 1);\n\n    for (size_t i = 0; i < REGISTER_4BYTES_SIZE; i++) {\n      sizeArr[REGISTER_4BYTES[i]] = setValue(4, 1, 1);\n    }\n    for (size_t i = 0; i < REGISTER_4BYTES_P2_SIZE; i++) {\n      sizeArr[REGISTER_4BYTES_P2[i]] = setValue(4, 2, 1);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES[i]] = setValue(8, 1, 1);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P2_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P2[i]] = setValue(8, 2, 1);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P2_S2_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P2_S2[i]] = setValue(8, 2, 2);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P3_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P3[i]] = setValue(8, 3, 1);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P3_S2_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P3_S2[i]] = setValue(8, 3, 2);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P4_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P4[i]] = setValue(8, 4, 1);\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_P4_S2_SIZE; i++) {\n      sizeArr[REGISTER_8BYTES_P4_S2[i]] = setValue(8, 4, 2);\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_SIZE; i++) {\n      sizeArr[REGISTER_16BYTES[i]] = setValue(16, 1, 1);\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_P2_SIZE; i++) {\n      sizeArr[REGISTER_16BYTES_P2[i]] = setValue(16, 2, 1);\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_P4_SIZE; i++) {\n      sizeArr[REGISTER_16BYTES_P4[i]] = setValue(16, 4, 1);\n    }\n    for (size_t i = 0; i < llvm::ARM::NUM_TARGET_REGS; i++) {\n      baseReg[i] = getEncodedBaseReg(i);\n    }\n  }\n\n  inline uint8_t getSize(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg < llvm::ARM::NUM_TARGET_REGS)\n      return sizeArr[reg] & 0xff;\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return 0;\n  }\n\n  inline uint8_t getPacked(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg < llvm::ARM::NUM_TARGET_REGS)\n      return (sizeArr[reg] >> 8) & 0xf;\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return 0;\n  }\n\n  inline uint8_t getSpaced(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg < llvm::ARM::NUM_TARGET_REGS)\n      return (sizeArr[reg] >> 12) & 0xf;\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return 0;\n  }\n\n  inline RegLLVM getUpperReg(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg >= llvm::ARM::NUM_TARGET_REGS) {\n      QBDI_ERROR(\"No register {}\", reg);\n      return llvm::ARM::NoRegister;\n    }\n    int8_t v = baseReg[reg];\n    if (v < 0) {\n      return llvm::ARM::NoRegister;\n    } else if (v <= 12) {\n      static_assert((llvm::ARM::R12 - llvm::ARM::R0) == 12);\n      return llvm::ARM::R0 + v;\n    } else if (v < 16) {\n      switch (v) {\n        case 13:\n          return llvm::ARM::SP;\n        case 14:\n          return llvm::ARM::LR;\n        case 15:\n          return llvm::ARM::PC;\n      }\n    } else if (v < 32) {\n      static_assert((llvm::ARM::Q15 - llvm::ARM::Q0) == 15);\n      return llvm::ARM::Q0 + (v - 16);\n    } else if (v < 64) {\n      static_assert((llvm::ARM::D31 - llvm::ARM::D0) == 31);\n      return llvm::ARM::D0 + (v - 32);\n    } else if (v < 96) {\n      static_assert((llvm::ARM::S31 - llvm::ARM::S0) == 31);\n      return llvm::ARM::S0 + (v - 64);\n    }\n    // invalid positive value\n    QBDI_ERROR(\"Wrong value {}\", v);\n    return llvm::ARM::NoRegister;\n  }\n\n  inline size_t getGPRPos(RegLLVM reg_) const {\n    unsigned reg = reg_.getValue();\n    if (reg >= llvm::ARM::NUM_TARGET_REGS) {\n      QBDI_ERROR(\"No register {}\", reg);\n      return -1;\n    }\n    int8_t v = baseReg[reg];\n    if (v < 0 || v > 15) {\n      return -1;\n    } else {\n      return v;\n    }\n  }\n\n  inline RegLLVM getUpperBasedRegister(RegLLVM reg) const {\n    RegLLVM r = getUpperReg(reg);\n    if (r == llvm::ARM::NoRegister) {\n      return reg;\n    }\n    return r;\n  }\n};\n\nconstexpr RegisterInfoArray arrayInfo;\n\n} // namespace\n\nuint8_t getRegisterSize(RegLLVM reg) { return arrayInfo.getSize(reg); }\n\nuint8_t getRegisterBaseOffset(RegLLVM reg) { return 0; }\n\nuint8_t getRegisterPacked(RegLLVM reg) { return arrayInfo.getPacked(reg); }\n\nuint8_t getRegisterSpaced(RegLLVM reg) { return arrayInfo.getSpaced(reg); }\n\nsize_t getGPRPosition(RegLLVM reg) { return arrayInfo.getGPRPos(reg); }\n\nRegLLVM getUpperRegister(RegLLVM reg, size_t pos) {\n  unsigned r = getPackedRegister(reg, pos).getValue();\n  if (llvm::ARM::S0 <= r and r <= llvm::ARM::S31) {\n    return llvm::ARM::Q0 + ((r - llvm::ARM::S0) >> 2);\n  } else if (llvm::ARM::D0 <= r and r <= llvm::ARM::D31) {\n    return llvm::ARM::Q0 + ((r - llvm::ARM::D0) >> 1);\n  } else {\n    return r;\n  }\n}\n\nRegLLVM getPackedRegister(RegLLVM reg, size_t pos) {\n  if (pos == 0) {\n    return arrayInfo.getUpperBasedRegister(reg);\n  }\n  if (pos >= getRegisterPacked(reg)) {\n    return llvm::ARM::NoRegister;\n  }\n  unsigned r = arrayInfo.getUpperBasedRegister(reg).getValue();\n  unsigned s = arrayInfo.getSpaced(reg) * pos;\n\n  if (llvm::ARM::Q0 <= r and r <= llvm::ARM::Q15) {\n    QBDI_REQUIRE_ABORT((r - llvm::ARM::Q0) + s <= 15, \"Unexpected Qregister {}\",\n                       (r - llvm::ARM::Q0) + s);\n    return r + s;\n  } else if (llvm::ARM::D0 <= r and r <= llvm::ARM::D31) {\n    QBDI_REQUIRE_ABORT((r - llvm::ARM::D0) + s <= 31, \"Unexpected Dregister {}\",\n                       (r - llvm::ARM::D0) + s);\n    return r + s;\n  } else if (llvm::ARM::R0 <= r and r <= llvm::ARM::R12) {\n    if ((r - llvm::ARM::R0) + s <= 12) {\n      return r + s;\n    } else if (r == llvm::ARM::R12 and s == 1) {\n      // llvm::ARM::R12_SP\n      return llvm::ARM::SP;\n    } else {\n      QBDI_ABORT(\"Unexpected register {}\", reg.getValue());\n    }\n  } else {\n    QBDI_ABORT(\"Unexpected register {}\", reg.getValue());\n  }\n}\n\nvoid fixLLVMUsedGPR(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                    std::array<RegisterUsage, NUM_GPR> &arr,\n                    std::map<RegLLVM, RegisterUsage> &m) {\n  switch (inst.getOpcode()) {\n    case llvm::ARM::BX_pred:\n      arr[REG_PC] |= RegisterSet;\n      break;\n    case llvm::ARM::BX_RET:\n      arr[REG_LR] |= RegisterUsed;\n      arr[REG_PC] |= RegisterSet;\n      break;\n    default:\n      break;\n  }\n\n  // CSPR register\n  if (m.count(Reg(REG_FLAG)) != 0) {\n    // when the instruction set the flags, we may mark the operand as a\n    // RegisterUsed look to the operandInfo and correct if this the case\n\n    const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n    const llvm::MCInstrDesc &desc = MCII.get(inst.getOpcode());\n    QBDI_REQUIRE_ABORT(desc.getNumOperands() <= inst.getNumOperands(),\n                       \"Unexpected operands number\");\n\n    for (unsigned int opn = 0; opn < desc.getNumOperands(); opn++) {\n      const llvm::MCOperandInfo &opInfo = desc.operands()[opn];\n      if (opInfo.RegClass == llvm::ARM::CCRRegClassID) {\n        // found the position of CSPR set operand\n        const llvm::MCOperand &op = inst.getOperand(opn);\n        QBDI_REQUIRE_ABORT(op.isReg(), \"Unexpected operand type\");\n\n        if (op.getReg() != llvm::ARM::NoRegister) {\n          // The instruction set the flags, recompute the CSPR flags\n          m[Reg(REG_FLAG)] = RegisterSet;\n          if (getCondition(inst, llvmcpu) != llvm::ARMCC::AL) {\n            // the instruction is conditionnal\n            m[Reg(REG_FLAG)] |= RegisterUsed;\n          }\n        }\n        break;\n      }\n    }\n  }\n\n  return;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/RelocatableInst_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdint.h>\n\n#include \"Target/ARM/Utils/ARMBaseInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"Patch/ARM/RelocatableInst_ARM.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\n// Generic RelocatableInst that must be implemented by each target\n\n// RelocTag\n// ========\n\nllvm::MCInst RelocTag::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  QBDI_ERROR(\"Internal Error: Relocate a Tag instruction.\");\n  if (cpumode == CPUMode::Thumb) {\n    return tmovr(Reg(8), Reg(8));\n  } else {\n    return nop();\n  }\n}\n\n// LoadShadow\n// ==========\n\nllvm::MCInst LoadShadow::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id = execBlock->getLastShadow(tag);\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n\n  return LoadDataBlockCC(reg, shadowOffset, llvm::ARMCC::AL)\n      .reloc(execBlock, cpumode);\n}\n\nint LoadShadow::getSize(const LLVMCPU &llvmcpu) const {\n  return LoadDataBlockCC(reg, 0xfff, llvm::ARMCC::AL).getSize(llvmcpu);\n}\n\n// StoreShadow\n// ===========\n\nllvm::MCInst StoreShadow::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id;\n  if (create) {\n    id = execBlock->newShadow(tag);\n  } else {\n    id = execBlock->getLastShadow(tag);\n  }\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n  return StoreDataBlockCC(reg, shadowOffset, llvm::ARMCC::AL)\n      .reloc(execBlock, cpumode);\n}\n\nint StoreShadow::getSize(const LLVMCPU &llvmcpu) const {\n  return StoreDataBlockCC(reg, 0xfff, llvm::ARMCC::AL).getSize(llvmcpu);\n}\n\n// LoadDataBlock\n// =============\n\nllvm::MCInst LoadDataBlock::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n\n  return LoadDataBlockCC(reg, offset, llvm::ARMCC::AL)\n      .reloc(execBlock, cpumode);\n}\n\nint LoadDataBlock::getSize(const LLVMCPU &llvmcpu) const {\n  return LoadDataBlockCC(reg, offset, llvm::ARMCC::AL).getSize(llvmcpu);\n}\n\n// StoreDataBlock\n// ==============\n\nllvm::MCInst StoreDataBlock::reloc(ExecBlock *execBlock,\n                                   CPUMode cpumode) const {\n  return StoreDataBlockCC(reg, offset, llvm::ARMCC::AL)\n      .reloc(execBlock, cpumode);\n}\n\nint StoreDataBlock::getSize(const LLVMCPU &llvmcpu) const {\n  return StoreDataBlockCC(reg, offset, llvm::ARMCC::AL).getSize(llvmcpu);\n}\n\n// MovReg\n// ======\n\nllvm::MCInst MovReg::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  return MovRegCC(dst, src, llvm::ARMCC::AL).reloc(execBlock, cpumode);\n}\n\nint MovReg::getSize(const LLVMCPU &llvmcpu) const {\n  return MovRegCC(dst, src, llvm::ARMCC::AL).getSize(llvmcpu);\n}\n\n// LoadImm\n// =======\n\nllvm::MCInst LoadImm::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  return LoadImmCC(reg, imm, llvm::ARMCC::AL).reloc(execBlock, cpumode);\n}\n\nint LoadImm::getSize(const LLVMCPU &llvmcpu) const {\n  return LoadImmCC(reg, imm, llvm::ARMCC::AL).getSize(llvmcpu);\n}\n\n// InstId\n// ======\n\nllvm::MCInst InstId::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t v = execBlock->getNextInstID();\n  return LoadImm(reg, v).reloc(execBlock, cpumode);\n}\n\nint InstId::getSize(const LLVMCPU &llvmcpu) const {\n  return LoadImm(reg, 0xffff).getSize(llvmcpu);\n}\n\n// Target Specific RelocatableInst\n\n// LoadShadowCC\n// ============\n\nllvm::MCInst LoadShadowCC::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id = execBlock->getLastShadow(tag);\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n\n  return LoadDataBlockCC(reg, shadowOffset, cond).reloc(execBlock, cpumode);\n}\n\nint LoadShadowCC::getSize(const LLVMCPU &llvmcpu) const {\n  return LoadDataBlockCC(reg, 0xfff, cond).getSize(llvmcpu);\n}\n\n// StoreShadowCC\n// =============\n\nllvm::MCInst StoreShadowCC::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id;\n  if (create) {\n    id = execBlock->newShadow(tag);\n  } else {\n    id = execBlock->getLastShadow(tag);\n  }\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n  return StoreDataBlockCC(reg, shadowOffset, cond).reloc(execBlock, cpumode);\n}\n\nint StoreShadowCC::getSize(const LLVMCPU &llvmcpu) const {\n  return StoreDataBlockCC(reg, 0xfff, cond).getSize(llvmcpu);\n}\n\n// LoadDataBlockCC\n// ===============\n\nllvm::MCInst LoadDataBlockCC::reloc(ExecBlock *execBlock,\n                                    CPUMode cpumode) const {\n\n  if (cpumode == CPUMode::Thumb) {\n    RegLLVM sr = execBlock->getScratchRegisterInfo().thumbScratchRegister;\n    return t2ldri12(reg, sr, offset, cond);\n  } else {\n    return ldri12(reg, Reg(REG_PC),\n                  execBlock->getDataBlockOffset() + offset - 8, cond);\n  }\n}\n\nint LoadDataBlockCC::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// StoreDataBlockCC\n// ================\n\nllvm::MCInst StoreDataBlockCC::reloc(ExecBlock *execBlock,\n                                     CPUMode cpumode) const {\n\n  if (cpumode == CPUMode::Thumb) {\n    RegLLVM sr = execBlock->getScratchRegisterInfo().thumbScratchRegister;\n    return t2stri12(reg, sr, offset, cond);\n  } else {\n    return stri12(reg, Reg(REG_PC),\n                  execBlock->getDataBlockOffset() + offset - 8, cond);\n  }\n}\n\nint StoreDataBlockCC::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// MovRegCC\n// ========\n\nllvm::MCInst MovRegCC::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  if (cpumode == CPUMode::Thumb) {\n    return tmovr(dst, src, cond);\n  } else {\n    return movr(dst, src, cond);\n  }\n}\n\nint MovRegCC::getSize(const LLVMCPU &llvmcpu) const {\n  if (llvmcpu.getCPUMode() == CPUMode::Thumb) {\n    return 2;\n  } else {\n    return 4;\n  }\n}\n\n// LoadImmCC\n// =========\n\nllvm::MCInst LoadImmCC::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  if (cpumode == CPUMode::Thumb) {\n    if (not t2moviCompatible(imm)) {\n      // ARMv6T2 has a t2MOVi16, however, llvm expects to have the feature v8m\n      // to enable this instruction.\n      //\n      // Avoid this instruction and used a shadow instead\n      uint16_t id = execBlock->newShadow();\n      execBlock->setShadow(id, imm);\n      rword offset = execBlock->getShadowOffset(id);\n      RegLLVM sr = execBlock->getScratchRegisterInfo().thumbScratchRegister;\n\n      return t2ldri12(reg, sr, offset, cond);\n    } else {\n      return t2movi(reg, imm, cond);\n    }\n  } else {\n    if (imm > 0xFFFF) {\n      uint16_t id = execBlock->newShadow();\n      execBlock->setShadow(id, imm);\n      rword offset = execBlock->getShadowOffset(id);\n\n      return ldri12(reg, Reg(REG_PC),\n                    execBlock->getDataBlockOffset() + offset - 8, cond);\n    } else {\n      return movi(reg, imm, cond);\n    }\n  }\n}\n\nint LoadImmCC::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// EpilogueBranch\n// ==============\n\nllvm::MCInst EpilogueBranch::reloc(ExecBlock *execBlock,\n                                   CPUMode cpumode) const {\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::ARM, \"Cannot be used in Thumb mode\");\n\n  rword target = execBlock->getEpilogueOffset() - 8;\n  QBDI_REQUIRE_ABORT(target % 4 == 0, \"Bad alignment!\");\n  return branch(target);\n}\n\nint EpilogueBranch::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// SetSREpilogue\n// =============\n\nllvm::MCInst SetSREpilogue::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb, \"Cannot be used in ARM mode\");\n  RegLLVM sr = execBlock->getScratchRegisterInfo().thumbScratchRegister;\n\n  return t2sub(sr, sr, execBlock->getEpilogueSize());\n}\n\nint SetSREpilogue::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// SRBranch\n// ========\n\nllvm::MCInst SRBranch::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  QBDI_REQUIRE_ABORT(cpumode == CPUMode::Thumb, \"Cannot be used in ARM mode\");\n\n  return tbx(execBlock->getScratchRegisterInfo().thumbScratchRegister);\n}\n\nint SRBranch::getSize(const LLVMCPU &llvmcpu) const { return 2; }\n\n// DataBlockAddress\n// ================\n\nllvm::MCInst DataBlockAddress::reloc(ExecBlock *execBlock,\n                                     CPUMode cpumode) const {\n  if (cpumode == CPUMode::Thumb) {\n    // EpilogueBranch may be used in the execBroker, avoid using sr\n    unsigned int offset = execBlock->getDataBlockOffset() - 4;\n\n    // The instruction Adr used Align(PC, 4). If the current instruction is not\n    // align, add 2\n    if (offset % 4 != 0) {\n      offset += 2;\n    }\n    QBDI_REQUIRE_ABORT(offset % 4 == 0, \"Invalid alignement\");\n    RegLLVM destReg = reg;\n    if (setScratchRegister) {\n      destReg = execBlock->getScratchRegisterInfo().thumbScratchRegister;\n    }\n    return t2adr(destReg, offset);\n  } else {\n    QBDI_REQUIRE_ABORT(not setScratchRegister,\n                       \"No scratch register in ARM mode\");\n\n    unsigned int offset = execBlock->getDataBlockOffset() - 8;\n    if (not armExpandCompatible(offset)) {\n      offset += 256 - (offset & 0xff);\n    }\n\n    return adr(reg, offset);\n  }\n}\n\nint DataBlockAddress::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// RelativeAddress\n// ===============\n\nllvm::MCInst RelativeAddress::reloc(ExecBlock *execBlock,\n                                    CPUMode cpumode) const {\n  if (cpumode == CPUMode::Thumb) {\n    if (execBlock->getCurrentPC() % 4 != 0) {\n      return t2adr(reg, offset - 2);\n    } else {\n      return t2adr(reg, offset - 4);\n    }\n  } else {\n    QBDI_REQUIRE_ABORT(armExpandCompatible(offset - 8),\n                       \"Offset not compatible with the instruction {}\",\n                       offset - 8);\n    return adr(reg, offset - 8);\n  }\n}\n\nint RelativeAddress::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// MovToSavedScratchReg\n// ====================\n\nllvm::MCInst MovToSavedScratchReg::reloc(ExecBlock *execBlock,\n                                         CPUMode cpumode) const {\n  if (cpumode == CPUMode::Thumb) {\n    if (candidateSC ==\n        execBlock->getScratchRegisterInfo().thumbScratchRegister) {\n      return StoreDataBlockCC(\n                 reg, offsetof(Context, hostState.scratchRegisterValue), cond)\n          .reloc(execBlock, cpumode);\n    } else {\n      // need a 4 bytes instruction as the size of an RelocatableInst should not\n      // depend of his position\n      return t2add(reg, candidateSC, 0, cond);\n    }\n  } else {\n    // no scratch register in ARM mode\n    return MovRegCC(reg, candidateSC, cond).reloc(execBlock, cpumode);\n  }\n}\n\nint MovToSavedScratchReg::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n// MovFromSavedScratchReg\n// ======================\n\nllvm::MCInst MovFromSavedScratchReg::reloc(ExecBlock *execBlock,\n                                           CPUMode cpumode) const {\n  if (cpumode == CPUMode::Thumb) {\n    if (candidateSC ==\n        execBlock->getScratchRegisterInfo().thumbScratchRegister) {\n      return LoadDataBlockCC(\n                 reg, offsetof(Context, hostState.scratchRegisterValue), cond)\n          .reloc(execBlock, cpumode);\n    } else {\n      // need a 4 bytes instruction as the size of an RelocatableInst should not\n      // depend of his position\n      return t2add(candidateSC, reg, 0, cond);\n    }\n  } else {\n    // no scratch register in ARM mode\n    return MovRegCC(candidateSC, reg, cond).reloc(execBlock, cpumode);\n  }\n}\n\nint MovFromSavedScratchReg::getSize(const LLVMCPU &llvmcpu) const { return 4; }\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/RelocatableInst_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef RELOCATABLEINST_ARM_H\n#define RELOCATABLEINST_ARM_H\n\n#include <memory>\n#include <utility>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass ExecBlock;\n\nclass LoadShadowCC : public AutoClone<RelocatableInst, LoadShadowCC> {\n  RegLLVM reg;\n  uint16_t tag;\n  unsigned cond;\n\npublic:\n  LoadShadowCC(RegLLVM reg, Shadow tag, unsigned cond)\n      : reg(reg), tag(tag.getTag()), cond(cond) {}\n\n  // Load a value from the last shadow with the given tag\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass StoreShadowCC : public AutoClone<RelocatableInst, StoreShadowCC> {\n  RegLLVM reg;\n  uint16_t tag;\n  bool create;\n  unsigned cond;\n\npublic:\n  StoreShadowCC(RegLLVM reg, Shadow tag, bool create, unsigned cond)\n      : reg(reg), tag(tag.getTag()), create(create), cond(cond) {}\n\n  // Store a value to a shadow\n  // if create, the shadow is create in the ExecBlock with the given tag\n  // otherwise, the last shadow with this tag is used\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass LoadDataBlockCC : public AutoClone<RelocatableInst, LoadDataBlockCC> {\n  RegLLVM reg;\n  int64_t offset;\n  unsigned cond;\n\npublic:\n  LoadDataBlockCC(RegLLVM reg, int64_t offset, unsigned cond)\n      : reg(reg), offset(offset), cond(cond) {}\n\n  // Load a value from the specified offset of the datablock\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass StoreDataBlockCC : public AutoClone<RelocatableInst, StoreDataBlockCC> {\n  RegLLVM reg;\n  int64_t offset;\n  unsigned cond;\n\npublic:\n  StoreDataBlockCC(RegLLVM reg, int64_t offset, unsigned cond)\n      : reg(reg), offset(offset), cond(cond) {}\n\n  // Store a value to the specified offset of the datablock\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass MovRegCC : public AutoClone<RelocatableInst, MovRegCC> {\n  RegLLVM dst;\n  RegLLVM src;\n  unsigned cond;\n\npublic:\n  MovRegCC(RegLLVM dst, RegLLVM src, unsigned cond)\n      : dst(dst), src(src), cond(cond) {}\n\n  // Move a value from a register to another\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass LoadImmCC : public AutoClone<RelocatableInst, LoadImmCC> {\n  RegLLVM reg;\n  Constant imm;\n  unsigned cond;\n\npublic:\n  LoadImmCC(RegLLVM reg, Constant imm, unsigned cond)\n      : reg(reg), imm(imm), cond(cond) {}\n\n  // Set the register to this value\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass EpilogueBranch : public AutoClone<RelocatableInst, EpilogueBranch> {\n\npublic:\n  EpilogueBranch() {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass SetSREpilogue : public AutoClone<RelocatableInst, SetSREpilogue> {\n\npublic:\n  SetSREpilogue() {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass SRBranch : public AutoClone<RelocatableInst, SRBranch> {\n\npublic:\n  SRBranch() {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass DataBlockAddress : public AutoClone<RelocatableInst, DataBlockAddress> {\n  RegLLVM reg;\n  bool setScratchRegister;\n\npublic:\n  /**\n   * Set DataBlock approximative address in reg\n   */\n  DataBlockAddress(RegLLVM reg) : reg(reg), setScratchRegister(false) {}\n\n  /**\n   * Set DataBlock approximative address in scratch register\n   */\n  DataBlockAddress() : reg(0), setScratchRegister(true) {}\n\n  /**\n   * ADR [reg/sr], imm\n   *\n   * NOTE for ARM mode:\n   *\n   * ADR instruction may not set the register at the begin of the\n   * datablock, as the encoding of the immediate don't allows any value.\n   *\n   * This relocatableInst must be follow by:\n   *  - BIC reg, reg, 0xff\n   * Used SetDataBlockAddress instead\n   */\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass RelativeAddress : public AutoClone<RelocatableInst, RelativeAddress> {\n  RegLLVM reg;\n  sword offset;\n\npublic:\n  /**\n   * Set DataBlock approximative address in reg\n   */\n  RelativeAddress(RegLLVM reg, sword offset) : reg(reg), offset(offset) {}\n\n  /**\n   * ADR [reg/sr], imm\n   */\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass MovToSavedScratchReg\n    : public AutoClone<RelocatableInst, MovToSavedScratchReg> {\n  RegLLVM reg;\n  Reg candidateSC;\n  unsigned cond;\n\npublic:\n  /**\n   * if candidateSC is the Scratch register, move the value hold by reg in the\n   * datablock\n   * else, move the value in candidateSC\n   */\n  MovToSavedScratchReg(RegLLVM reg, Reg candidateSC, unsigned cond)\n      : reg(reg), candidateSC(candidateSC), cond(cond) {}\n\n  /**\n   */\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass MovFromSavedScratchReg\n    : public AutoClone<RelocatableInst, MovFromSavedScratchReg> {\n  RegLLVM reg;\n  Reg candidateSC;\n  unsigned cond;\n\npublic:\n  /**\n   * if candidateSC is the Scratch register, load the real value of the scratch\n   * register from the datablock to the reg\n   * else, move the value from candidateSC to reg\n   */\n  MovFromSavedScratchReg(Reg candidateSC, RegLLVM reg, unsigned cond)\n      : reg(reg), candidateSC(candidateSC), cond(cond) {}\n\n  /**\n   */\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\n// class RelocateStratchRegister : public AutoClone<RelocatableInst,\n// RelocateStratchRegister> {\n//   llvm::MCInst inst;\n//   unsigned int replaceReg;\n//\n// public:\n//   RelocateStratchRegister(llvm::MCInst&& inst, unsigned int replaceReg) :\n//   inst(inst), replaceReg(replaceReg) {}\n//\n//   llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n// };\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/TempManagerImpl_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef TempManager_ARM_H\n#define TempManager_ARM_H\n\n#include <set>\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nstatic const std::set<Reg> TempManagerUnrestoreGPR = {};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/Patch/ARM/TempManager_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <set>\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nvoid TempManager::generateSaveRestoreInstructions(\n    unsigned unrestoredRegNum, RelocatableInst::UniquePtrVec &saveInst,\n    RelocatableInst::UniquePtrVec &restoreInst, Reg::Vec &unrestoredReg) const {\n\n  saveInst.clear();\n  restoreInst.clear();\n  unrestoredReg.clear();\n\n  Reg::Vec usedRegisters = getUsedRegisters();\n\n  for (Reg r : usedRegisters) {\n    if (shouldRestore(r)) {\n      append(saveInst, SaveReg(r, Offset(r)).genReloc(*patch.llvmcpu));\n      if (unrestoredReg.size() < unrestoredRegNum) {\n        unrestoredReg.push_back(r);\n      } else {\n        append(restoreInst, LoadReg(r, Offset(r)).genReloc(*patch.llvmcpu));\n      }\n    } else {\n      unrestoredReg.push_back(r);\n    }\n  }\n}\n\nvoid allocateConsecutiveTempRegister(TempManager &temp_manager, Temp temp1,\n                                     Temp temp2) {\n  const Patch &patch = temp_manager.getPatch();\n\n  QBDI_REQUIRE_ABORT(not temp_manager.isAllocatedId(temp1),\n                     \"Temp is already register {}\", patch);\n  QBDI_REQUIRE_ABORT(not temp_manager.isAllocatedId(temp2),\n                     \"Temp is already register {}\", patch);\n\n  for (int i = 0; i < 12; i += 2) {\n    if (temp_manager.usedRegister(Reg(i)) or\n        temp_manager.usedRegister(Reg(i + 1)) or patch.regUsage[i] != 0 or\n        patch.regUsage[i + 1] != 0) {\n      continue;\n    }\n    temp_manager.associatedReg(temp1, Reg(i));\n    temp_manager.associatedReg(temp2, Reg(i + 1));\n    return;\n  }\n  QBDI_ABORT(\"Fail to allocate consecutive TempRegister {}\", patch);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/ARM/TempManager_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef TempManager_ARM_H\n#define TempManager_ARM_H\n\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\n// allows to select 2 temp register where the selected register is\n// reg1 + 1 == reg2 and with reg1 % 2 in [r0,r2,r4,r6,r8,r10]\nvoid allocateConsecutiveTempRegister(TempManager &temp_manager, Temp temp1,\n                                     Temp temp2);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/CMakeLists.txt",
    "content": "# Add QBDI target\n\nif(QBDI_ARCH_X86 OR QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nendif()\n\nset(SOURCES\n    \"${CMAKE_CURRENT_LIST_DIR}/InstrRule.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstrRules.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstTransform.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Patch.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchCondition.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchGenerator.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchRule.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Register.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/TempManager.cpp\")\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Patch/ExecBlockFlags.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef ExecBlockFlags_H\n#define ExecBlockFlags_H\n\n#include <memory>\n#include <vector>\n\n#include \"QBDI/Platform.h\"\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n#include \"Patch/X86_64/ExecBlockFlags_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"Patch/ARM/ExecBlockFlags_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"Patch/AARCH64/ExecBlockFlags_AARCH64.h\"\n#endif\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\nclass LLVMCPU;\n\nuint8_t getExecBlockFlags(const llvm::MCInst &inst, const LLVMCPU &llvmcpu);\n\nextern const uint8_t defaultExecuteFlags;\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/ExecBlockPatch.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef EXECBLOCKPATCH_H\n#define EXECBLOCKPATCH_H\n\n#include <memory>\n#include <vector>\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nclass PatchRule;\nclass RelocatableInst;\nclass LLVMCPU;\n\nstd::vector<std::unique_ptr<RelocatableInst>>\ngetExecBlockPrologue(const LLVMCPU &llvmcpu);\n\nstd::vector<std::unique_ptr<RelocatableInst>>\ngetExecBlockEpilogue(const LLVMCPU &llvmcpu);\n\nstd::vector<std::unique_ptr<RelocatableInst>>\ngetTerminator(const LLVMCPU &llvmcpu, rword address);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/InstInfo.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTINFO_H\n#define INSTINFO_H\n\n#include <stdint.h>\n#include \"QBDI/State.h\"\n\nnamespace llvm {\nclass MCInst;\n} // namespace llvm\n\nnamespace QBDI {\nclass LLVMCPU;\n\nunsigned getInstSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu);\nunsigned getReadSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu);\nunsigned getWriteSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu);\nunsigned getImmediateSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu);\nsword getFixedOperandValue(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                           unsigned index, int64_t value);\n\nbool unsupportedRead(const llvm::MCInst &inst);\nbool unsupportedWrite(const llvm::MCInst &inst);\n\nbool variadicOpsIsWrite(const llvm::MCInst &inst);\n\n}; // namespace QBDI\n\n#endif // INSTCLASSES_H\n"
  },
  {
    "path": "src/Patch/InstMetadata.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTMETADATA_H\n#define INSTMETADATA_H\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"Utility/InstAnalysis_prive.h\"\n\n#include \"QBDI/State.h\"\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n#include \"Patch/X86_64/InstMetadata_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"Patch/ARM/InstMetadata_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"Patch/AARCH64/InstMetadata_AARCH64.h\"\n#endif\n\nnamespace QBDI {\n\nclass InstMetadata {\npublic:\n  llvm::MCInst inst;\n  rword address;\n  uint32_t instSize;\n  uint32_t patchSize;\n  CPUMode cpuMode;\n  bool modifyPC;\n  uint8_t execblockFlags;\n  mutable InstAnalysisPtr analysis;\n  InstMetadataArch archMetadata;\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  // prefix for X86_64 instruction like ``lock``\n  std::vector<llvm::MCInst> prefix;\n#endif\n\n  InstMetadata(const llvm::MCInst &inst, rword address, uint32_t instSize,\n               uint32_t patchSize, CPUMode cpuMode, bool modifyPC,\n               uint8_t execblockFlags, InstAnalysisPtr analysis)\n      : inst(inst), address(address), instSize(instSize), patchSize(patchSize),\n        cpuMode(cpuMode), modifyPC(modifyPC), execblockFlags(execblockFlags),\n        analysis(std::move(analysis)) {}\n\n  InstMetadata(const llvm::MCInst &inst, rword address, uint32_t instSize,\n               CPUMode cpuMode, uint8_t execblockFlags)\n      : inst(inst), address(address), instSize(instSize), patchSize(0),\n        cpuMode(cpuMode), modifyPC(false), execblockFlags(execblockFlags),\n        analysis(nullptr) {}\n\n  inline rword endAddress() const { return address + instSize; }\n\n  inline InstMetadata lightCopy() const {\n    InstMetadata cpy{inst,    address,  instSize,       patchSize,\n                     cpuMode, modifyPC, execblockFlags, nullptr};\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n    cpy.prefix = prefix;\n#endif\n    cpy.archMetadata = archMetadata;\n    return cpy;\n  }\n};\n\n} // namespace QBDI\n\n#endif // INSTMETADATA_H\n"
  },
  {
    "path": "src/Patch/InstTransform.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdlib.h>\n#include <utility>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstTransform.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/TempManager.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nvoid SetOperand::transform(llvm::MCInst &inst, rword address, size_t instSize,\n                           TempManager &temp_manager) const {\n  QBDI_REQUIRE_ABORT(opn < inst.getNumOperands(), \"Invalid operand {} {}\", opn,\n                     temp_manager.getPatch());\n  switch (type) {\n    case TempOperandType:\n      inst.getOperand(opn).setReg(temp_manager.getRegForTemp(temp).getValue());\n      break;\n    case RegOperandType:\n      inst.getOperand(opn).setReg(reg.getValue());\n      break;\n    case ImmOperandType:\n      inst.getOperand(opn).setImm(imm);\n      break;\n  }\n}\n\nvoid SubstituteWithTemp::transform(llvm::MCInst &inst, rword address,\n                                   size_t instSize,\n                                   TempManager &temp_manager) const {\n  for (unsigned int i = 0; i < inst.getNumOperands(); i++) {\n    llvm::MCOperand &op = inst.getOperand(i);\n    if (op.isReg() && op.getReg() == ((RegLLVM)reg)) {\n      op.setReg(temp_manager.getRegForTemp(temp).getValue());\n    }\n  }\n}\n\nvoid AddOperand::transform(llvm::MCInst &inst, rword address, size_t instSize,\n                           TempManager &temp_manager) const {\n  switch (type) {\n    case TempOperandType:\n      inst.insert(inst.begin() + opn,\n                  llvm::MCOperand::createReg(\n                      temp_manager.getRegForTemp(temp).getValue()));\n      break;\n    case RegOperandType:\n      inst.insert(inst.begin() + opn,\n                  llvm::MCOperand::createReg(reg.getValue()));\n      break;\n    case ImmOperandType:\n      inst.insert(inst.begin() + opn, llvm::MCOperand::createImm(imm));\n      break;\n    case CpyOperandType:\n      QBDI_REQUIRE_ABORT(src < inst.getNumOperands(), \"Invalid operand {} {}\",\n                         src, temp_manager.getPatch());\n      if (inst.getOperand(src).isReg()) {\n        inst.insert(inst.begin() + opn,\n                    llvm::MCOperand::createReg(inst.getOperand(src).getReg()));\n      } else if (inst.getOperand(src).isImm()) {\n        inst.insert(inst.begin() + opn,\n                    llvm::MCOperand::createImm(inst.getOperand(src).getImm()));\n      } else {\n        QBDI_ABORT(\"Unexpected operand type {}\", temp_manager.getPatch());\n      }\n      break;\n  }\n}\n\nvoid RemoveOperand::transform(llvm::MCInst &inst, rword address,\n                              size_t instSize,\n                              TempManager &temp_manager) const {\n  switch (type) {\n    case RegType:\n      for (auto it = inst.begin(); it != inst.end(); ++it) {\n        if (it->isReg() && it->getReg() == reg) {\n          inst.erase(it);\n          break;\n        }\n      }\n      break;\n    case OperandType:\n      inst.erase(inst.begin() + opn);\n      break;\n  }\n}\n\nvoid SetOpcode::transform(llvm::MCInst &inst, rword address, size_t instSize,\n                          TempManager &temp_manager) const {\n  inst.setOpcode(opcode);\n}\n\nvoid ReplaceOpcode::transform(llvm::MCInst &inst, rword address,\n                              size_t instSize,\n                              TempManager &temp_manager) const {\n  const auto it = opcode.find(inst.getOpcode());\n  QBDI_REQUIRE_ABORT(it != opcode.end(), \"Opcode not found {}\",\n                     temp_manager.getPatch());\n  inst.setOpcode(it->second);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/InstTransform.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTTRANSFORM_H\n#define INSTTRANSFORM_H\n\n#include <map>\n#include <memory>\n#include <stddef.h>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\nclass TempManager;\n\nclass InstTransform {\npublic:\n  using UniquePtr = std::unique_ptr<InstTransform>;\n  using UniquePtrVec = std::vector<std::unique_ptr<InstTransform>>;\n\n  virtual std::unique_ptr<InstTransform> clone() const = 0;\n\n  virtual void transform(llvm::MCInst &inst, rword address, size_t instSize,\n                         TempManager &temp_manager) const = 0;\n\n  virtual ~InstTransform() = default;\n};\n\nclass SetOperand : public AutoClone<InstTransform, SetOperand> {\n  Operand opn;\n  enum { TempOperandType, RegOperandType, ImmOperandType } type;\n  // Not working VS 2015\n  // union {\n  Temp temp;\n  Reg reg;\n  Constant imm;\n  // };\n\npublic:\n  /*! Set the operand opn of the instruction as the Temp temp.\n   *\n   * @param[in] opn   Operand index in the LLVM MCInst representation.\n   * @param[in] temp  Temporary register which will be set as the new operand\n   */\n  SetOperand(Operand opn, Temp temp)\n      : opn(opn), type(TempOperandType), temp(temp), reg(0), imm(0) {}\n\n  /*! Set the operand opn of the instruction as the Reg reg.\n   *\n   * @param[in] opn  Operand index in the LLVM MCInst representation.\n   * @param[in] reg  Register which will be set as the new operand.\n   */\n  SetOperand(Operand opn, Reg reg)\n      : opn(opn), type(RegOperandType), temp(0), reg(reg), imm(0) {}\n\n  /*! Set the operand opn of the instruction as the immediate imm.\n   *\n   * @param[in] opn  Operand index in the LLVM MCInst representation.\n   * @param[in] imm  Constant which will be set as the new immediate operand.\n   */\n  SetOperand(Operand opn, Constant imm)\n      : opn(opn), type(ImmOperandType), temp(0), reg(0), imm(imm) {}\n\n  void transform(llvm::MCInst &inst, rword address, size_t instSize,\n                 TempManager &temp_manager) const override;\n};\n\nclass SubstituteWithTemp : public AutoClone<InstTransform, SubstituteWithTemp> {\n  Reg reg;\n  Temp temp;\n\npublic:\n  /*! Substitute every reference to reg in the operands of the instruction with\n   * temp.\n   *\n   * @param[in] reg   Register which will be substituted.\n   * @param[in] temp  Temporary register which will be substituted with.\n   */\n  SubstituteWithTemp(Reg reg, Temp temp) : reg(reg), temp(temp) {};\n\n  void transform(llvm::MCInst &inst, rword address, size_t instSize,\n                 TempManager &temp_manager) const override;\n};\n\nclass AddOperand : public AutoClone<InstTransform, AddOperand> {\n\n  Operand opn;\n  enum { TempOperandType, RegOperandType, ImmOperandType, CpyOperandType } type;\n  // Not working under VS2015\n  // union {\n  Temp temp;\n  Reg reg;\n  Constant imm;\n  Operand src;\n  // };\n\npublic:\n  /*! Add a new temporary register operand to the instruction by inserting it at\n   * operand index opn.\n   *\n   * @param[in] opn   Operand index in LLVM MCInst representation.\n   * @param[in] temp  Temp to be inserted as a new operand.\n   */\n  AddOperand(Operand opn, Temp temp)\n      : opn(opn), type(TempOperandType), temp(temp), reg(0), imm(0), src(0) {}\n\n  /*! Add a new register operand to the instruction by inserting it at operand\n   * index opn.\n   *\n   * @param[in] opn  Operand index in LLVM MCInst representation.\n   * @param[in] reg  Register to be inserted as a new operand.\n   */\n  AddOperand(Operand opn, Reg reg)\n      : opn(opn), type(RegOperandType), temp(0), reg(reg), imm(0), src(0) {}\n\n  /*! Add a new immediate operand to the instruction by inserting it at operand\n   * index opn.\n   *\n   * @param[in] opn  Operand index in LLVM MCInst representation.\n   * @param[in] imm  Constant to be inserted as a new immediate operand.\n   */\n  AddOperand(Operand opn, Constant imm)\n      : opn(opn), type(ImmOperandType), temp(0), reg(0), imm(imm), src(0) {}\n\n  /*! Copy operand src to opn\n   *\n   * @param[in] opn  Operand index in LLVM MCInst representation.\n   * @param[in] src  Operand to copy\n   */\n  AddOperand(Operand opn, Operand src)\n      : opn(opn), type(CpyOperandType), temp(0), reg(0), imm(0), src(src) {}\n\n  void transform(llvm::MCInst &inst, rword address, size_t instSize,\n                 TempManager &temp_manager) const override;\n};\n\nclass RemoveOperand : public AutoClone<InstTransform, RemoveOperand> {\n\n  Reg reg;\n  Operand opn;\n  enum { OperandType, RegType } type;\n\npublic:\n  /*! Remove the first occurence of reg in the operands of the instruction.\n   *\n   * @param[in] reg Register to remove from the operand list.\n   */\n  RemoveOperand(Reg reg) : reg(reg), opn(0), type(RegType) {}\n\n  /*! Remove the operand n from the list of operand\n   *\n   * @param[in] opn   Operand to to remove from the operand list.\n   */\n  RemoveOperand(Operand opn) : reg(0), opn(opn), type(OperandType) {}\n\n  void transform(llvm::MCInst &inst, rword address, size_t instSize,\n                 TempManager &temp_manager) const override;\n};\n\nclass SetOpcode : public AutoClone<InstTransform, SetOpcode> {\n\n  unsigned int opcode;\n\npublic:\n  /*! Set the opcode of the instruction.\n   *\n   * @param[in] opcode New opcode to set as the instruction opcode.\n   */\n  SetOpcode(unsigned int opcode) : opcode(opcode) {}\n\n  void transform(llvm::MCInst &inst, rword address, size_t instSize,\n                 TempManager &temp_manager) const override;\n};\n\nclass ReplaceOpcode : public AutoClone<InstTransform, ReplaceOpcode> {\n\n  std::map<unsigned, unsigned> opcode;\n\npublic:\n  /*! Set the opcode of the instruction.\n   *\n   * @param[in] opcode New opcode to set as the instruction opcode.\n   */\n  ReplaceOpcode(std::map<unsigned, unsigned> opcode) : opcode(opcode) {}\n\n  void transform(llvm::MCInst &inst, rword address, size_t instSize,\n                 TempManager &temp_manager) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/InstrRule.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdint.h>\n#include <stdlib.h>\n#include <utility>\n\n#include \"Engine/VM_internal.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/InstrRule.h\"\n#include \"Patch/InstrRules.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/InstAnalysis_prive.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\n// InstrRule\n// =========\n\nvoid InstrRule::instrument(Patch &patch,\n                           const PatchGenerator::UniquePtrVec &patchGen,\n                           bool breakToHost, InstPosition position,\n                           int priority, RelocatableInstTag tag) const {\n\n  if (patchGen.size() == 0 && breakToHost == false) {\n    QBDI_DEBUG(\"Empty patch Generator\");\n    return;\n  }\n\n  /* The instrument function needs to handle several different cases. An\n   * instrumentation can be either prepended or appended to the patch and, in\n   * each case, can trigger a break to host.\n   */\n  RelocatableInst::UniquePtrVec instru;\n  TempManager tempManager(patch);\n\n  // Generate the instrumentation code from the original instruction context\n  for (const PatchGenerator::UniquePtr &g : patchGen) {\n    append(instru, g->generate(patch, tempManager));\n  }\n\n  // In case we break to the host, we need to ensure the value of PC in the\n  // context is correct. This value needs to be set when instrumenting before\n  // the instruction or when instrumenting after an instruction which does not\n  // set PC.\n  if (breakToHost) {\n    if (position == InstPosition::PREINST || patch.metadata.modifyPC == false) {\n      rword address;\n      switch (position) {\n        // In PREINST PC is set to the current address\n        case InstPosition::PREINST:\n          address = patch.metadata.address;\n          break;\n        // In POSTINST PC is set to the next instruction address\n        case InstPosition::POSTINST:\n          address = patch.metadata.endAddress();\n          break;\n        default:\n          QBDI_ABORT(\"Invalid position {} {}\", position, patch);\n      }\n#if defined(QBDI_ARCH_ARM)\n      if (patch.metadata.cpuMode == CPUMode::Thumb) {\n        address |= 1;\n      }\n#endif\n      append(\n          instru,\n          GetConstant(Temp(0), Constant(address)).generate(patch, tempManager));\n      append(instru, SaveReg(tempManager.getRegForTemp(0), Offset(Reg(REG_PC)))\n                         .genReloc(*patch.llvmcpu));\n    }\n  }\n\n  // The breakToHost code requires one temporary register. If none were\n  // allocated by the instrumentation we thus need to add one.\n  if (breakToHost && tempManager.getUsedRegisterNumber() == 0) {\n    tempManager.getRegForTemp(Temp(0));\n  }\n\n  RelocatableInst::UniquePtrVec saveReg, restoreReg;\n  Reg::Vec unrestoredReg;\n\n  // In the break to host case the first used register is not restored and\n  // instead given to the break to host code as a scratch. It will later be\n  // restored by the break to host code.\n  if (breakToHost) {\n    tempManager.generateSaveRestoreInstructions(1, saveReg, restoreReg,\n                                                unrestoredReg);\n    QBDI_REQUIRE(unrestoredReg.size() >= 1);\n\n    prepend(instru, std::move(saveReg));\n    append(instru, std::move(restoreReg));\n    append(instru, getBreakToHost(unrestoredReg[0], patch,\n                                  tempManager.shouldRestore(unrestoredReg[0])));\n  }\n  // Normal case where we append the temporary register restoration code to the\n  // instrumentation\n  else {\n    tempManager.generateSaveRestoreInstructions(0, saveReg, restoreReg,\n                                                unrestoredReg);\n    prepend(instru, std::move(saveReg));\n    append(instru, std::move(restoreReg));\n  }\n\n  // add Tag\n  instru.insert(instru.begin(), RelocTag::unique(tag));\n\n  QBDI_DEBUG(\n      \"Insert {} PatchGen with priority {}, position {} ({}) and tag 0x{:x}\",\n      instru.size(), priority,\n      (position == PREINST) ? \"PREINST\"\n                            : ((position == POSTINST) ? \"POSTINST\" : \"\"),\n      position, tag);\n\n  // Add the result to the patch\n  // The result is added in a pending list that is sorted by priority\n  // The pending list is flush in the Patch when all InstrRule has been apply\n  patch.addInstsPatch(position, priority, std::move(instru));\n}\n\n// InstrRuleBasicCBK\n// =================\n\nInstrRuleBasicCBK::InstrRuleBasicCBK(PatchConditionUniquePtr &&condition,\n                                     InstCallback cbk, void *data,\n                                     InstPosition position, bool breakToHost,\n                                     int priority, RelocatableInstTag tag)\n    : AutoUnique<InstrRule, InstrRuleBasicCBK>(priority),\n      condition(std::forward<PatchConditionUniquePtr>(condition)),\n      patchGen(getCallbackGenerator(cbk, data)), position(position),\n      breakToHost(breakToHost), tag(tag), cbk(cbk), data(data) {}\n\nInstrRuleBasicCBK::~InstrRuleBasicCBK() = default;\n\nbool InstrRuleBasicCBK::canBeApplied(const Patch &patch,\n                                     const LLVMCPU &llvmcpu) const {\n  return condition->test(patch, llvmcpu);\n}\n\nbool InstrRuleBasicCBK::changeDataPtr(void *new_data) {\n  data = new_data;\n  patchGen = getCallbackGenerator(cbk, data);\n  return true;\n}\n\nstd::unique_ptr<InstrRule> InstrRuleBasicCBK::clone() const {\n  return InstrRuleBasicCBK::unique(condition->clone(), cbk, data, position,\n                                   breakToHost, priority);\n};\n\nRangeSet<rword> InstrRuleBasicCBK::affectedRange() const {\n  return condition->affectedRange();\n}\n\n// InstrRuleDynamic\n// ================\n\nInstrRuleDynamic::InstrRuleDynamic(PatchConditionUniquePtr &&condition,\n                                   PatchGenMethod patchGenMethod,\n                                   InstPosition position, bool breakToHost,\n                                   int priority, RelocatableInstTag tag)\n    : AutoUnique<InstrRule, InstrRuleDynamic>(priority),\n      condition(std::forward<PatchConditionUniquePtr>(condition)),\n      patchGenMethod(patchGenMethod), position(position),\n      breakToHost(breakToHost), tag(tag) {}\n\nInstrRuleDynamic::~InstrRuleDynamic() = default;\n\nbool InstrRuleDynamic::canBeApplied(const Patch &patch,\n                                    const LLVMCPU &llvmcpu) const {\n  return condition->test(patch, llvmcpu);\n}\n\nstd::unique_ptr<InstrRule> InstrRuleDynamic::clone() const {\n  return InstrRuleDynamic::unique(condition->clone(), patchGenMethod, position,\n                                  breakToHost, priority);\n};\n\nRangeSet<rword> InstrRuleDynamic::affectedRange() const {\n  return condition->affectedRange();\n}\n\n// InstrRuleUser\n// =============\n\nInstrRuleUser::InstrRuleUser(InstrRuleCallback cbk, AnalysisType analysisType_,\n                             void *cbk_data, VMInstanceRef vm,\n                             RangeSet<rword> range, int priority)\n    : AutoClone<InstrRule, InstrRuleUser>(priority), cbk(cbk),\n      analysisType(analysisType_), cbk_data(cbk_data), vm(vm),\n      range(std::move(range)) {\n\n  if ((analysisType & AnalysisType::ANALYSIS_JIT) != 0) {\n    QBDI_WARN(\"Can't use analysis type ANALYSIS_JIT with InstrRuleCallback\");\n    analysisType ^= AnalysisType::ANALYSIS_JIT;\n  }\n}\n\nInstrRuleUser::~InstrRuleUser() = default;\n\nbool InstrRuleUser::tryInstrument(Patch &patch, const LLVMCPU &llvmcpu) const {\n  if (!range.contains(Range<rword>(\n          patch.metadata.address,\n          patch.metadata.address + patch.metadata.instSize, real_addr_t()))) {\n    return false;\n  }\n\n  QBDI_DEBUG(\"Call user InstrCB at {} with analysisType 0x{:x}\",\n             reinterpret_cast<void *>(cbk), analysisType);\n\n  const InstAnalysis *ana =\n      analyzeInstMetadata(patch.metadata, analysisType, llvmcpu);\n\n  std::vector<InstrRuleDataCBK> vec = cbk(vm, ana, cbk_data);\n\n  QBDI_DEBUG(\"InstrCB return {} callback(s)\", vec.size());\n\n  if (vec.size() == 0) {\n    return false;\n  }\n\n  for (const InstrRuleDataCBK &cbkToAdd : vec) {\n    if (cbkToAdd.priority == PRIORITY_MEMACCESS_LIMIT ||\n        cbkToAdd.priority == PRIORITY_MEMACCESS_LIMIT + 1) {\n      QBDI_WARN(\n          \"Using priority {} may conflict with QBDI internal callback for \"\n          \"memory access\",\n          cbkToAdd.priority);\n    }\n    if (cbkToAdd.lambdaCbk == nullptr) {\n      instrument(patch, getCallbackGenerator(cbkToAdd.cbk, cbkToAdd.data), true,\n                 cbkToAdd.position, cbkToAdd.priority,\n                 (cbkToAdd.position == PREINST) ? RelocTagPreInstStdCBK\n                                                : RelocTagPostInstStdCBK);\n    } else {\n      patch.userInstCB.emplace_back(\n          std::make_unique<InstCbLambda>(cbkToAdd.lambdaCbk));\n      instrument(patch,\n                 getCallbackGenerator(InstCBLambdaProxy,\n                                      patch.userInstCB.back().get()),\n                 true, cbkToAdd.position, cbkToAdd.priority,\n                 (cbkToAdd.position == PREINST) ? RelocTagPreInstStdCBK\n                                                : RelocTagPostInstStdCBK);\n    }\n  }\n\n  return true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/InstrRule.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTRRULE_H\n#define INSTRRULE_H\n\n#include <algorithm>\n#include <memory>\n#include <vector>\n\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nclass LLVMCPU;\nclass Patch;\nclass PatchCondition;\nclass PatchGenerator;\n\nusing PatchConditionUniquePtr = std::unique_ptr<PatchCondition>;\nusing PatchGeneratorUniquePtrVec = std::vector<std::unique_ptr<PatchGenerator>>;\n\n/*! An instrumentation rule written in PatchDSL.\n */\nclass InstrRule {\n\nprotected:\n  // priority of the rule.\n  // The rule with the lesser priority will be applied first\n  int priority;\n\npublic:\n  InstrRule(int priority = PRIORITY_DEFAULT) : priority(priority) {}\n\n  virtual ~InstrRule() = default;\n\n  // virtual copy constructor used to duplicate the object\n  virtual std::unique_ptr<InstrRule> clone() const = 0;\n\n  virtual RangeSet<rword> affectedRange() const = 0;\n\n  inline int getPriority() const { return priority; };\n\n  inline void setPriority(int priority) { this->priority = priority; };\n\n  inline virtual void changeVMInstanceRef(VMInstanceRef vminstance) {};\n\n  inline virtual bool changeDataPtr(void *data) { return false; };\n\n  /*! Determine wheter this rule have to be apply on this Path and instrument if\n   * needed.\n   *\n   * @param[in] patch     The current patch to instrument.\n   * @param[in] llvmcpu   LLVMCPU object\n   */\n  virtual bool tryInstrument(Patch &patch, const LLVMCPU &llvmcpu) const = 0;\n\n  /*! Instrument a patch by evaluating its generators on the current context.\n   * Also handles the temporary register management for this patch.\n   *\n   * @param[in] patch       The current patch to instrument.\n   * @param[in] patchGen    The list of patchGenerator to apply\n   * @param[in] breakToHost Add a break to VM need to be add after the patch\n   * @param[in] position    Add the patch before or after the instruction\n   * @param[in] priority    The priority of this patch\n   * @param[in] tag         The tag for this patch\n   */\n  void instrument(Patch &patch, const PatchGeneratorUniquePtrVec &patchGen,\n                  bool breakToHost, InstPosition position, int priority,\n                  RelocatableInstTag tag) const;\n};\n\nclass InstrRuleBasicCBK : public AutoUnique<InstrRule, InstrRuleBasicCBK> {\n\n  PatchConditionUniquePtr condition;\n  PatchGeneratorUniquePtrVec patchGen;\n  InstPosition position;\n  bool breakToHost;\n  RelocatableInstTag tag;\n  InstCallback cbk;\n  void *data;\n\npublic:\n  /*! Allocate a new instrumentation rule with a condition, a list of\n   * generators, an instrumentation position and a breakToHost request.\n   *\n   * @param[in] condition    A PatchCondition which determine wheter or not this\n   *                         PatchRule applies.\n   * @param[in] cbk          The callback to call\n   * @param[in] data         The data pointer to give to the callback\n   * @param[in] position     An enum indicating wether this instrumentation\n   *                         should be positioned before the instruction or\n   *                         after it.\n   * @param[in] breakToHost  A boolean determining whether this instrumentation\n   *                         should end with a break to host (in the case of a\n   *                         callback for example).\n   * @param[in] priority     Priority of the callback\n   * @param[in] tag          A tag for the callback\n   */\n  InstrRuleBasicCBK(PatchConditionUniquePtr &&condition, InstCallback cbk,\n                    void *data, InstPosition position, bool breakToHost,\n                    int priority = PRIORITY_DEFAULT,\n                    RelocatableInstTag tag = RelocTagInvalid);\n\n  ~InstrRuleBasicCBK() override;\n\n  std::unique_ptr<InstrRule> clone() const override;\n\n  inline InstPosition getPosition() const { return position; }\n\n  RangeSet<rword> affectedRange() const override;\n\n  /*! Determine wheter this rule applies by evaluating this rule condition on\n   * the current context.\n   *\n   * @param[in] patch     A patch containing the current context.\n   * @param[in] llvmcpu   LLVMCPU object\n   *\n   * @return True if this instrumentation condition evaluate to true on this\n   * patch.\n   */\n  bool canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const;\n\n  bool changeDataPtr(void *data) override;\n\n  inline bool tryInstrument(Patch &patch,\n                            const LLVMCPU &llvmcpu) const override {\n    if (canBeApplied(patch, llvmcpu)) {\n      instrument(patch, patchGen, breakToHost, position, priority, tag);\n      return true;\n    }\n    return false;\n  }\n};\n\ntypedef const PatchGeneratorUniquePtrVec &(*PatchGenMethod)(\n    Patch &patch, const LLVMCPU &llvmcpu);\n\nclass InstrRuleDynamic : public AutoUnique<InstrRule, InstrRuleDynamic> {\n\n  PatchConditionUniquePtr condition;\n  PatchGenMethod patchGenMethod;\n  InstPosition position;\n  bool breakToHost;\n  RelocatableInstTag tag;\n\npublic:\n  /*! Allocate a new instrumentation rule with a condition, a method to generate\n   * patch instruction, an instrumentation position and a breakToHost request.\n   *\n   * @param[in] condition        A PatchCondition which determine wheter or not\n   *                             this PatchRule applies.\n   * @param[in] patchGenMethod   A Method that will be called to generate the\n   *                             patch.\n   * @param[in] position         An enum indicating wether this instrumentation\n   *                             should be positioned before the instruction or\n   *                             after it.\n   * @param[in] breakToHost      A boolean determining whether this\n   *                             instrumentation should end with a break to\n   *                             host (in the case of a callback for example).\n   * @param[in] priority         Priority of the callback\n   * @param[in] tag              A tag for the callback\n   */\n  InstrRuleDynamic(PatchConditionUniquePtr &&condition,\n                   PatchGenMethod patchGenMethod, InstPosition position,\n                   bool breakToHost, int priority = PRIORITY_DEFAULT,\n                   RelocatableInstTag tag = RelocTagInvalid);\n\n  ~InstrRuleDynamic() override;\n\n  std::unique_ptr<InstrRule> clone() const override;\n\n  inline InstPosition getPosition() const { return position; }\n\n  RangeSet<rword> affectedRange() const override;\n\n  /*! Determine wheter this rule applies by evaluating this rule condition on\n   * the current context.\n   *\n   * @param[in] patch     A patch containing the current context.\n   * @param[in] llvmcpu   LLVMCPU object\n   *\n   * @return True if this instrumentation condition evaluate to true on this\n   * patch.\n   */\n  bool canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const;\n\n  inline bool tryInstrument(Patch &patch,\n                            const LLVMCPU &llvmcpu) const override {\n    if (canBeApplied(patch, llvmcpu)) {\n      instrument(patch, patchGenMethod(patch, llvmcpu), breakToHost, position,\n                 priority, tag);\n      return true;\n    }\n    return false;\n  }\n};\n\nclass InstrRuleUser : public AutoClone<InstrRule, InstrRuleUser> {\n\n  InstrRuleCallback cbk;\n  AnalysisType analysisType;\n  void *cbk_data;\n  VMInstanceRef vm;\n  RangeSet<rword> range;\n\npublic:\n  InstrRuleUser(InstrRuleCallback cbk, AnalysisType analysisType,\n                void *cbk_data, VMInstanceRef vm, RangeSet<rword> range,\n                int priority = 0);\n\n  ~InstrRuleUser() override;\n\n  inline void changeVMInstanceRef(VMInstanceRef vminstance) override {\n    vm = vminstance;\n  };\n\n  inline bool changeDataPtr(void *data) override {\n    cbk_data = data;\n    return true;\n  };\n\n  inline RangeSet<rword> affectedRange() const override { return range; }\n\n  bool tryInstrument(Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/InstrRules.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stddef.h>\n\n#include \"QBDI/State.h\"\n#include \"ExecBlock/Context.h\"\n#include \"Patch/InstrRules.h\"\n#include \"Patch/PatchGenerator.h\"\n\nnamespace QBDI {\n\n/*! Output a list of PatchGenerator which would set up the host state part of\n * the context for a callback.\n *\n * @param[in] cbk   The callback function to call.\n * @param[in] data  The data to pass as an argument to the callback function.\n *\n * @return A list of PatchGenerator to set up this callback call.\n *\n */\nPatchGenerator::UniquePtrVec getCallbackGenerator(InstCallback cbk,\n                                                  void *data) {\n  PatchGenerator::UniquePtrVec callbackGenerator;\n\n  // Write callback address in host state\n  callbackGenerator.push_back(\n      GetConstant::unique(Temp(0), Constant((rword)cbk)));\n  callbackGenerator.push_back(WriteTemp::unique(\n      Temp(0), Offset(offsetof(Context, hostState.callback))));\n  // Write callback data pointer in host state\n  callbackGenerator.push_back(\n      GetConstant::unique(Temp(0), Constant((rword)data)));\n  callbackGenerator.push_back(\n      WriteTemp::unique(Temp(0), Offset(offsetof(Context, hostState.data))));\n  // Write internal instruction id of a callback\n  callbackGenerator.push_back(GetInstId::unique(Temp(0)));\n  callbackGenerator.push_back(\n      WriteTemp::unique(Temp(0), Offset(offsetof(Context, hostState.origin))));\n\n  return callbackGenerator;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/InstrRules.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTRRULES_H\n#define INSTRRULES_H\n\n#include <memory>\n#include <vector>\n\n#include \"Patch/Types.h\"\n\n#include \"QBDI/Callback.h\"\n\nnamespace QBDI {\nclass Patch;\nclass PatchGenerator;\nclass RelocatableInst;\n\n/*\n * Setup a user callback in the host state\n *\n * Created patch generator can be used in any instruction rules.\n *\n * @param[in] cbk   Pointer to a user callback\n * @param[in] data  Opaque pointer to user callback data\n */\nstd::vector<std::unique_ptr<PatchGenerator>>\ngetCallbackGenerator(InstCallback cbk, void *data);\n\nstd::vector<std::unique_ptr<RelocatableInst>>\ngetBreakToHost(Reg temp, const Patch &patch, bool restore);\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/MemoryAccess.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCH_MEMORYACCESS_H\n#define PATCH_MEMORYACCESS_H\n\n#include \"Patch/InstrRule.h\"\n\n#include \"QBDI/Callback.h\"\n\nnamespace QBDI {\n\nclass ExecBlock;\nclass LLVMCPU;\n\nvoid analyseMemoryAccess(const ExecBlock &currentExecBlock, uint16_t instID,\n                         bool afterInst, std::vector<MemoryAccess> &dest);\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessRead();\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessWrite();\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/Patch.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <iterator>\n#include <utility>\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/ExecBlockFlags.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"llvm/MC/MCInst.h\"\n\nnamespace QBDI {\n\nPatch::Patch(const llvm::MCInst &inst, rword address, uint32_t instSize,\n             const LLVMCPU &llvmcpu)\n    : metadata(inst, address, instSize, llvmcpu.getCPUMode(),\n               getExecBlockFlags(inst, llvmcpu)),\n      regUsage({RegisterUnused}), llvmcpu(&llvmcpu), finalize(false) {\n  metadata.patchSize = 0;\n\n  getUsedGPR(metadata.inst, llvmcpu, regUsage, regUsageExtra);\n}\n\nPatch::~Patch() = default;\n\nPatch::Patch(Patch &&) = default;\n\nPatch &Patch::operator=(Patch &&) = default;\n\nvoid Patch::setModifyPC(bool modifyPC) { metadata.modifyPC = modifyPC; }\n\nvoid Patch::append(RelocatableInst::UniquePtr &&r) {\n  insts.push_back(std::forward<RelocatableInst::UniquePtr>(r));\n  metadata.patchSize += 1;\n}\n\nvoid Patch::append(RelocatableInst::UniquePtrVec v) {\n  metadata.patchSize += v.size();\n  if (insts.empty()) {\n    insts.swap(v);\n  } else {\n    std::move(v.begin(), v.end(), std::back_inserter(insts));\n  }\n}\n\nvoid Patch::prepend(RelocatableInst::UniquePtr &&r) {\n  insts.insert(insts.begin(), std::forward<RelocatableInst::UniquePtr>(r));\n  metadata.patchSize += 1;\n  patchGenFlagsOffset += 1;\n}\n\nvoid Patch::prepend(RelocatableInst::UniquePtrVec v) {\n  if (not v.empty()) {\n    metadata.patchSize += v.size();\n    patchGenFlagsOffset += v.size();\n    // front iterator on std::vector may need to move all value at each move\n    // use a back_inserter in v and swap the vector at the end\n    std::move(insts.begin(), insts.end(), std::back_inserter(v));\n    v.swap(insts);\n  }\n}\n\nvoid Patch::insertAt(unsigned position, RelocatableInst::UniquePtrVec v) {\n  if (not v.empty()) {\n    metadata.patchSize += v.size();\n    for (auto &p : patchGenFlags) {\n      if (p.first >= position) {\n        p.first += v.size();\n      }\n    }\n    std::move(\n        v.begin(), v.end(),\n        std::inserter(insts, insts.begin() + position + patchGenFlagsOffset));\n  }\n}\n\nvoid Patch::addInstsPatch(InstPosition position, int priority,\n                          std::vector<std::unique_ptr<RelocatableInst>> v) {\n  QBDI_REQUIRE(not finalize);\n\n  InstrPatch el{position, priority, std::move(v)};\n\n  auto it = std::upper_bound(instsPatchs.begin(), instsPatchs.end(), el,\n                             [](const InstrPatch &a, const InstrPatch &b) {\n                               return a.priority > b.priority;\n                             });\n  instsPatchs.insert(it, std::move(el));\n}\n\nvoid Patch::finalizeInstsPatch() {\n  QBDI_REQUIRE(not finalize);\n  // avoid to used prepend\n  RelocatableInst::UniquePtrVec prePatch;\n  // add the tag RelocTagPatchInstBegin\n  prePatch.push_back(RelocTag::unique(RelocTagPatchBegin));\n\n  // The begin of the patch is a target for the prologue.\n  auto v = TargetPrologue().genReloc(*this);\n  std::move(v.begin(), v.end(), std::back_inserter(prePatch));\n\n  // Add PREINST callback by priority order\n  for (InstrPatch &el : instsPatchs) {\n    if (el.position == PREINST) {\n      RelocatableInst::UniquePtrVec v;\n      v.swap(el.insts);\n      std::move(v.begin(), v.end(), std::back_inserter(prePatch));\n    } else if (el.position == POSTINST) {\n      continue;\n    } else {\n      QBDI_ABORT(\"Invalid position 0x{:x} {}\", el.position, *this);\n    }\n  }\n\n  // add the tag RelocTagPatchInstBegin\n  prePatch.push_back(RelocTag::unique(RelocTagPatchInstBegin));\n  // prepend the current RelocInst to the Patch\n  prepend(std::move(prePatch));\n  // add the tag RelocTagPatchInstEnd\n  append(RelocTag::unique(RelocTagPatchInstEnd));\n\n  // append TargetPrologue for SKIP_INST\n  append(TargetPrologue().genReloc(*this));\n\n  // Add POSTINST callback by priority order\n  for (InstrPatch &el : instsPatchs) {\n    if (el.position == PREINST) {\n      continue;\n    } else if (el.position == POSTINST) {\n      RelocatableInst::UniquePtrVec v;\n      v.swap(el.insts);\n      append(std::move(v));\n    } else {\n      QBDI_ABORT(\"Invalid position 0x{:x} {}\", el.position, *this);\n    }\n  }\n\n  instsPatchs.clear();\n  finalize = true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/Patch.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCH_H\n#define PATCH_H\n\n#include <array>\n#include <map>\n#include <memory>\n#include <set>\n#include <vector>\n\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/State.h\"\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\nclass LLVMCPU;\nclass RelocatableInst;\n\nstruct InstrPatch {\n  InstPosition position;\n  int priority;\n  std::vector<std::unique_ptr<RelocatableInst>> insts;\n};\n\nclass Patch {\nprivate:\n  std::vector<InstrPatch> instsPatchs;\n\npublic:\n  InstMetadata metadata;\n  std::vector<std::unique_ptr<RelocatableInst>> insts;\n  // flags generate by the PatchRule\n  std::vector<std::pair<unsigned int, int>> patchGenFlags;\n  int patchGenFlagsOffset = 0;\n  // InstCbLambda to register in the ExecBlockManager\n  std::vector<std::unique_ptr<InstCbLambda>> userInstCB;\n  // Registers Used and Defs by the instruction\n  std::array<RegisterUsage, NUM_GPR> regUsage;\n  std::map<RegLLVM, RegisterUsage> regUsageExtra;\n  // Registers used by the TempRegister for this patch\n  std::set<RegLLVM> tempReg;\n  const LLVMCPU *llvmcpu;\n  bool finalize = false;\n\n  using Vec = std::vector<Patch>;\n\n  Patch(const llvm::MCInst &inst, rword address, uint32_t instSize,\n        const LLVMCPU &llvmcpu);\n\n  Patch(Patch &&);\n  Patch &operator=(Patch &&);\n\n  ~Patch();\n\n  void setModifyPC(bool modifyPC);\n\n  void append(std::unique_ptr<RelocatableInst> &&r);\n  void append(std::vector<std::unique_ptr<RelocatableInst>> v);\n\n  void prepend(std::unique_ptr<RelocatableInst> &&r);\n  void prepend(std::vector<std::unique_ptr<RelocatableInst>> v);\n\n  void insertAt(unsigned position,\n                std::vector<std::unique_ptr<RelocatableInst>> v);\n\n  void addInstsPatch(InstPosition position, int priority,\n                     std::vector<std::unique_ptr<RelocatableInst>> v);\n\n  void finalizeInstsPatch();\n};\n\n} // namespace QBDI\n\n#endif // PATCH_H\n"
  },
  {
    "path": "src/Patch/PatchCondition.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Utility/String.h\"\n\nnamespace QBDI {\n\nbool MnemonicIs::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return QBDI::String::startsWith(\n      mnemonic.c_str(), llvmcpu.getInstOpcodeName(patch.metadata.inst));\n}\n\nbool OpIs::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return patch.metadata.inst.getOpcode() == op;\n}\n\nbool UseReg::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  for (unsigned int i = 0; i < patch.metadata.inst.getNumOperands(); i++) {\n    const llvm::MCOperand &op = patch.metadata.inst.getOperand(i);\n    if (op.isReg() && op.getReg() == ((RegLLVM)reg)) {\n      return true;\n    }\n  }\n  return false;\n}\n\nbool DoesReadAccess::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return getReadSize(patch.metadata.inst, llvmcpu) > 0;\n}\n\nbool DoesWriteAccess::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return getWriteSize(patch.metadata.inst, llvmcpu) > 0;\n}\n\nbool HasOptions::test(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return llvmcpu.hasOptions(opts);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/PatchCondition.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHCONDITION_H\n#define PATCHCONDITION_H\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\n#include \"QBDI/Options.h\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\nclass LLVMCPU;\n\nclass PatchCondition {\npublic:\n  using UniquePtr = std::unique_ptr<PatchCondition>;\n  using UniquePtrVec = std::vector<std::unique_ptr<PatchCondition>>;\n\n  virtual UniquePtr clone() const = 0;\n\n  virtual bool test(const Patch &patch, const LLVMCPU &llvmcpu) const = 0;\n\n  virtual RangeSet<rword> affectedRange() const {\n    RangeSet<rword> r;\n    r.add(Range<rword>(0, (rword)-1, real_addr_t()));\n    return r;\n  }\n\n  virtual ~PatchCondition() = default;\n};\n\nclass MnemonicIs : public AutoClone<PatchCondition, MnemonicIs> {\n  std::string mnemonic;\n\npublic:\n  /*! Return true if the mnemonic of the current instruction is equal to\n   * Mnemonic.\n   *\n   * @param[in] mnemonic   A null terminated instruction mnemonic (using LLVM\n   * style)\n   */\n  MnemonicIs(const char *mnemonic) : mnemonic(mnemonic) {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass OpIs : public AutoClone<PatchCondition, OpIs> {\n  unsigned int op;\n\npublic:\n  /*! Return true if the instruction opcode is equal to op.\n   *\n   * @param[in] op LLVM instruction opcode ID.\n   */\n  OpIs(unsigned int op) : op(op) {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass UseReg : public AutoClone<PatchCondition, UseReg> {\n  Reg reg;\n\npublic:\n  /*!  Return true if the instruction uses reg as one of its operand.\n   *\n   * @param[in] reg The register to compare with.\n   */\n  UseReg(Reg reg) : reg(reg) {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass InstructionInRange\n    : public AutoClone<PatchCondition, InstructionInRange> {\n  Range<rword> range;\n\npublic:\n  /*! Return true if the instruction address is in the range [start, end[ (end\n   * not included).\n   *\n   * @param[in] start Start of the range.\n   * @param[in] end   End of the range (not included).\n   */\n  InstructionInRange(Constant start, Constant end)\n      : range(start, end, real_addr_t()) {\n    // for ARM, remove the LSB\n    if constexpr (is_arm) {\n      range.setStart(range.start() & (~1));\n    }\n  }\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {\n    return range.contains(Range<rword>(\n        patch.metadata.address, patch.metadata.endAddress(), real_addr_t()));\n  }\n\n  RangeSet<rword> affectedRange() const override {\n    RangeSet<rword> r;\n    r.add(range);\n    return r;\n  }\n};\n\nclass AddressIs : public AutoClone<PatchCondition, AddressIs> {\n  rword breakpoint;\n\npublic:\n  /*! Return true if on specified address\n   */\n  AddressIs(rword breakpoint_) : breakpoint(breakpoint_) {\n    // for ARM, remove the LSB\n    if constexpr (is_arm) {\n      breakpoint &= (~1);\n    }\n  }\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {\n    return patch.metadata.address == breakpoint;\n  }\n\n  RangeSet<rword> affectedRange() const override {\n    RangeSet<rword> r;\n    r.add(Range<rword>(breakpoint, breakpoint + 1, real_addr_t()));\n    return r;\n  }\n};\n\nclass And : public AutoUnique<PatchCondition, And> {\n  PatchCondition::UniquePtrVec conditions;\n\npublic:\n  /*! Return true if every PatchCondition of the list conditions return true\n   * (lazy evaluation).\n   *\n   * @param[in] conditions List of conditions to evaluate.\n   */\n  And(PatchCondition::UniquePtrVec &&conditions)\n      : conditions(std::forward<PatchCondition::UniquePtrVec>(conditions)) {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {\n    return std::all_of(conditions.begin(), conditions.end(),\n                       [&](const PatchCondition::UniquePtr &cond) {\n                         return cond->test(patch, llvmcpu);\n                       });\n  }\n\n  RangeSet<rword> affectedRange() const override {\n    RangeSet<rword> r;\n    r.add(Range<rword>(0, (rword)-1, real_addr_t()));\n    for (unsigned int i = 0; i < conditions.size(); i++) {\n      r.intersect(conditions[i]->affectedRange());\n    }\n    return r;\n  }\n\n  inline std::unique_ptr<PatchCondition> clone() const override {\n    return And::unique(cloneVec(conditions));\n  };\n};\n\nclass Or : public AutoUnique<PatchCondition, Or> {\n  PatchCondition::UniquePtrVec conditions;\n\npublic:\n  /*! Return true if one of the PatchCondition of the list conditions return\n   * true (lazy evaluation).\n   *\n   * @param[in] conditions List of conditions to evaluate.\n   */\n  Or(PatchCondition::UniquePtrVec &&conditions)\n      : conditions(std::forward<PatchCondition::UniquePtrVec>(conditions)) {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {\n    return std::any_of(conditions.begin(), conditions.end(),\n                       [&](const PatchCondition::UniquePtr &cond) {\n                         return cond->test(patch, llvmcpu);\n                       });\n  }\n\n  RangeSet<rword> affectedRange() const override {\n    RangeSet<rword> r;\n    for (unsigned int i = 0; i < conditions.size(); i++) {\n      r.add(conditions[i]->affectedRange());\n    }\n    return r;\n  }\n\n  inline std::unique_ptr<PatchCondition> clone() const override {\n    return Or::unique(cloneVec(conditions));\n  };\n};\n\nclass Not : public AutoUnique<PatchCondition, Not> {\n  PatchCondition::UniquePtr condition;\n\npublic:\n  /*! Return the logical inverse of condition.\n   *\n   * @param[in] condition Condition to evaluate.\n   */\n  Not(PatchCondition::UniquePtr &&condition)\n      : condition(std::forward<PatchCondition::UniquePtr>(condition)) {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {\n    return !condition->test(patch, llvmcpu);\n  }\n\n  inline std::unique_ptr<PatchCondition> clone() const override {\n    return Not::unique(condition->clone());\n  };\n};\n\nclass True : public AutoClone<PatchCondition, True> {\npublic:\n  /*! Return true.\n   */\n  True() {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {\n    return true;\n  }\n};\n\nclass DoesReadAccess : public AutoClone<PatchCondition, DoesReadAccess> {\npublic:\n  /*! Return true if the instruction read data from memory.\n   */\n  DoesReadAccess() {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass DoesWriteAccess : public AutoClone<PatchCondition, DoesWriteAccess> {\npublic:\n  /*! Return true if the instruction write data to memory.\n   */\n  DoesWriteAccess() {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\nclass HasOptions : public AutoClone<PatchCondition, HasOptions> {\n  Options opts;\n\npublic:\n  /*! Return true if the options is specified\n   */\n  HasOptions(Options opts_) : opts(opts_) {}\n\n  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/PatchGenerator.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdlib.h>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"QBDI/Platform.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/InstTransform.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\ntemplate <typename T>\nRelocatableInst::UniquePtrVec\nPureEval<T>::generate(const Patch &patch, TempManager &temp_manager) const {\n  return this->genReloc(*patch.llvmcpu);\n}\n\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, PatchGenFlags>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, LoadReg>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, SaveReg>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\ntemplate RelocatableInst::UniquePtrVec\nPureEval<AutoClone<PatchGenerator, JmpEpilogue>>::generate(\n    const Patch &patch, TempManager &temp_manager) const;\n\n// ModifyInstruction\n// =================\n\nModifyInstruction::ModifyInstruction(InstTransform::UniquePtrVec &&transforms)\n    : transforms(std::forward<InstTransform::UniquePtrVec>(transforms)) {};\n\nstd::unique_ptr<PatchGenerator> ModifyInstruction::clone() const {\n  return ModifyInstruction::unique(cloneVec(transforms));\n};\n\nRelocatableInst::UniquePtrVec\nModifyInstruction::generate(const Patch &patch,\n                            TempManager &temp_manager) const {\n\n  llvm::MCInst a(patch.metadata.inst);\n  for (const auto &t : transforms) {\n    t->transform(a, patch.metadata.address, patch.metadata.instSize,\n                 temp_manager);\n  }\n  return conv_unique<RelocatableInst>(NoReloc::unique(std::move(a)));\n}\n\n// PatchGenFlags\n// =============\n\nRelocatableInst::UniquePtrVec\nPatchGenFlags::genReloc(const LLVMCPU &llvmcpu) const {\n  return {};\n}\n\n// GetOperand\n// ==========\n\nRelocatableInst::UniquePtrVec\nGetOperand::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  Reg destReg = reg;\n  if (type == TmpType) {\n    destReg = temp_manager.getRegForTemp(temp);\n  }\n  QBDI_REQUIRE_ABORT(op < inst.getNumOperands(), \"Invalid operand {} {}\", op,\n                     patch);\n  if (inst.getOperand(op).isReg()) {\n    return conv_unique<RelocatableInst>(\n        MovReg::unique(destReg, inst.getOperand(op).getReg()));\n  } else if (inst.getOperand(op).isImm()) {\n    return conv_unique<RelocatableInst>(\n        LoadImm::unique(destReg, Constant(inst.getOperand(op).getImm())));\n  } else {\n    QBDI_ERROR(\"Invalid operand type for GetOperand()\");\n    return {};\n  }\n}\n\n// WriteOperand\n// ============\n\nRelocatableInst::UniquePtrVec\nWriteOperand::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n\n  QBDI_REQUIRE_ABORT(op < inst.getNumOperands(), \"Invalid operand {} {}\", op,\n                     patch);\n  if (inst.getOperand(op).isReg()) {\n    return conv_unique<RelocatableInst>(\n        StoreDataBlock::unique(inst.getOperand(op).getReg(), offset));\n  } else {\n    QBDI_ERROR(\"Invalid operand type for WriteOperand()\");\n    return {};\n  }\n}\n\n// GetConstant\n// ===========\n\nRelocatableInst::UniquePtrVec\nGetConstant::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  return conv_unique<RelocatableInst>(\n      LoadImm::unique(temp_manager.getRegForTemp(temp), cst));\n}\n\n// GetConstantMap\n// ==============\n\nRelocatableInst::UniquePtrVec\nGetConstantMap::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const auto it = opcodeMap.find(inst.getOpcode());\n  QBDI_REQUIRE_ABORT(it != opcodeMap.end(), \"Opcode not found {}\",\n                     temp_manager.getPatch());\n\n  return conv_unique<RelocatableInst>(\n      LoadImm::unique(temp_manager.getRegForTemp(temp), it->second));\n}\n\n// ReadTemp\n// ========\n\nRelocatableInst::UniquePtrVec\nReadTemp::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  if (type == OffsetType) {\n    return conv_unique<RelocatableInst>(\n        LoadDataBlock::unique(temp_manager.getRegForTemp(temp), offset));\n  } else if (type == ShadowType) {\n    return conv_unique<RelocatableInst>(\n        LoadShadow::unique(temp_manager.getRegForTemp(temp), shadow));\n  }\n  _QBDI_UNREACHABLE();\n}\n\n// WriteTemp\n// =========\n\nRelocatableInst::UniquePtrVec\nWriteTemp::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  if (type == OffsetType) {\n    return conv_unique<RelocatableInst>(\n        StoreDataBlock::unique(temp_manager.getRegForTemp(temp), offset));\n  } else if (type == ShadowType) {\n    return conv_unique<RelocatableInst>(\n        StoreShadow::unique(temp_manager.getRegForTemp(temp), shadow, true));\n  } else if (type == OperandType) {\n    const llvm::MCInst &inst = patch.metadata.inst;\n    QBDI_REQUIRE_ABORT(operand < inst.getNumOperands(), \"Invalid operand {} {}\",\n                       operand, patch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(operand).isReg(),\n                       \"Unexpected operand type {}\", patch);\n    int regNo = getGPRPosition(inst.getOperand(operand).getReg());\n    QBDI_REQUIRE_ABORT(regNo != -1, \"Unexpected GPRregister {} {}\",\n                       inst.getOperand(operand).getReg(), patch);\n    return conv_unique<RelocatableInst>(\n        MovReg::unique(Reg(regNo), temp_manager.getRegForTemp(temp)));\n  }\n  _QBDI_UNREACHABLE();\n}\n\n// LoadReg\n// =======\n\nRelocatableInst::UniquePtrVec LoadReg::genReloc(const LLVMCPU &llvmcpu) const {\n\n  return conv_unique<RelocatableInst>(LoadDataBlock::unique(reg, offset));\n}\n\n// SaveTemp\n// ========\n\nRelocatableInst::UniquePtrVec\nSaveTemp::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  Reg reg = temp_manager.getRegForTemp(temp);\n  if ((not checkManager) or temp_manager.shouldRestore(reg)) {\n    return conv_unique<RelocatableInst>(\n        StoreDataBlock::unique(reg, reg.offset()));\n  } else {\n    return {};\n  }\n}\n\n// SaveReg\n// =======\n\nRelocatableInst::UniquePtrVec SaveReg::genReloc(const LLVMCPU &llvmcpu) const {\n\n  return conv_unique<RelocatableInst>(StoreDataBlock::unique(reg, offset));\n}\n\n// CopyReg\n// =======\n\nRelocatableInst::UniquePtrVec\nCopyReg::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  if (type == Reg2Temp) {\n    return conv_unique<RelocatableInst>(\n        MovReg::unique(temp_manager.getRegForTemp(destTemp), src));\n  } else if (type == Reg2Reg) {\n    return conv_unique<RelocatableInst>(MovReg::unique(destReg, src));\n  }\n  _QBDI_UNREACHABLE();\n}\n\n// CopyTemp\n// ========\n\nRelocatableInst::UniquePtrVec\nCopyTemp::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  if (type == Temp2Temp) {\n    return conv_unique<RelocatableInst>(MovReg::unique(\n        temp_manager.getRegForTemp(destTemp), temp_manager.getRegForTemp(src)));\n  } else if (type == Temp2Reg) {\n    return conv_unique<RelocatableInst>(\n        MovReg::unique(destReg, temp_manager.getRegForTemp(src)));\n  }\n  _QBDI_UNREACHABLE();\n}\n\n// GetInstId\n// =========\n\nRelocatableInst::UniquePtrVec\nGetInstId::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  return conv_unique<RelocatableInst>(\n      InstId::unique(temp_manager.getRegForTemp(temp)));\n}\n\n// TargetPrologue\n// ==============\n\nRelocatableInst::UniquePtrVec\nTargetPrologue::generate(const Patch &patch, TempManager &temp_manager) const {\n  return genReloc(patch);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/PatchGenerator.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHGENERATOR_H\n#define PATCHGENERATOR_H\n\n#include <map>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/InstTransform.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\nclass InstTransform;\nclass LLVMCPU;\nclass Patch;\nclass RelocatableInst;\nclass TempManager;\n\n/* Flags value for PatchGenerator\n */\nenum PatchGeneratorFlags {\n  None = 0,\n  PatchRuleBegin = 0x1,\n  PatchRuleEnd = 0x2,\n  ModifyInstructionBeginFlags = 0x3,\n  ModifyInstructionEndFlags = 0x4,\n  ArchSpecificFlags = 0x80\n};\n\nclass PatchGenerator {\npublic:\n  using UniquePtr = std::unique_ptr<PatchGenerator>;\n  using UniquePtrVec = std::vector<std::unique_ptr<PatchGenerator>>;\n\n  virtual std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const = 0;\n\n  virtual ~PatchGenerator() = default;\n\n  virtual std::unique_ptr<PatchGenerator> clone() const = 0;\n\n  virtual inline bool modifyPC() const { return false; }\n\n  virtual inline uint32_t getPreFlags() const {\n    return PatchGeneratorFlags::None;\n  }\n  virtual inline uint32_t getPostFlags() const {\n    return PatchGeneratorFlags::None;\n  }\n};\n\ntemplate <typename T>\nclass PureEval : public T {\npublic:\n  virtual std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const = 0;\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const final;\n};\n\n// Generic Generator available for any target\n\nclass ModifyInstruction : public AutoUnique<PatchGenerator, ModifyInstruction> {\n  std::vector<std::unique_ptr<InstTransform>> transforms;\n\npublic:\n  /*! Apply a list of InstTransform to the current instruction and output the\n   * result.\n   *\n   * @param[in] transforms Vector of InstTransform to be applied.\n   */\n  ModifyInstruction(std::vector<std::unique_ptr<InstTransform>> &&transforms);\n\n  std::unique_ptr<PatchGenerator> clone() const override;\n\n  /*!\n   * Output:\n   *   (depends on the current instructions and transforms)\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline uint32_t getPreFlags() const override {\n    return PatchGeneratorFlags::ModifyInstructionBeginFlags;\n  }\n  inline uint32_t getPostFlags() const override {\n    return PatchGeneratorFlags::ModifyInstructionEndFlags;\n  }\n};\n\nclass PatchGenFlags\n    : public PureEval<AutoClone<PatchGenerator, PatchGenFlags>> {\n  uint32_t flags;\n\npublic:\n  PatchGenFlags(uint32_t flags) : flags(flags) {};\n\n  /*!\n   * Output:\n   *   (none)\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n\n  inline uint32_t getPreFlags() const override { return flags; }\n};\n\nclass GetOperand : public AutoClone<PatchGenerator, GetOperand> {\n  Temp temp;\n  Reg reg;\n  Operand op;\n\n  enum { TmpType, RegType } type;\n\npublic:\n  /*! Obtain the value of the operand op and copy it's value in a temporary. If\n   * op is an immediate the immediate value is copied, if op is a register the\n   * register value is copied.\n   *\n   * @param[in] temp   A temporary where the value will be copied.\n   * @param[in] op     The operand index (relative to the instruction\n   *                   LLVM MCInst representation) to be copied.\n   */\n  GetOperand(Temp temp, Operand op)\n      : temp(temp), reg(0), op(op), type(TmpType) {}\n\n  /*! Obtain the value of the operand op and copy it's value in a register. If\n   * op is an immediate the immediate value is copied, if op is a register the\n   * register value is copied.\n   *\n   * @param[in] reg    The register where the value will be copied.\n   * @param[in] op     The operand index (relative to the instruction\n   *                   LLVM MCInst representation) to be copied.\n   */\n  GetOperand(Reg reg, Operand op) : temp(0), reg(reg), op(op), type(RegType) {}\n\n  /*!\n   * Output:\n   *   MOV REG64 temp, IMM64/REG64 op\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass WriteOperand : public AutoClone<PatchGenerator, WriteOperand> {\n  Operand op;\n  Offset offset;\n\npublic:\n  /*! Obtain the value of the operand op and copy it's value to the Datablock\n   * Only Register operand is supported\n   *\n   * @param[in] op      The operand index (relative to the instruction\n   *                    LLVM MCInst representation) to be copied.\n   * @param[in] offset  The offset in the data block where the operand\n   *                    will be written.\n   */\n  WriteOperand(Operand op, Offset offset) : op(op), offset(offset) {}\n\n  /*!\n   * Output:\n   *   MOV REG64 temp, IMM64/REG64 op\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override {\n    return offset == Offset(Reg(REG_PC));\n  }\n};\n\nclass GetConstant : public AutoClone<PatchGenerator, GetConstant> {\n  Temp temp;\n  Constant cst;\n\npublic:\n  /*! Copy a constant in a temporary.\n   *\n   * @param[in] temp   A temporary where the value will be copied.\n   * @param[in] cst    The constant to copy.\n   */\n  GetConstant(Temp temp, Constant cst) : temp(temp), cst(cst) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, IMM64 cst\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetConstantMap : public AutoClone<PatchGenerator, GetConstantMap> {\n  Temp temp;\n  std::map<unsigned, Constant> opcodeMap;\n\npublic:\n  /*! Set the opcode of the instruction.\n   *\n   * @param[in] opcode New opcode to set as the instruction opcode.\n   */\n  GetConstantMap(Temp temp, std::map<unsigned, Constant> opcodeMap)\n      : temp(temp), opcodeMap(opcodeMap) {}\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass ReadTemp : public AutoClone<PatchGenerator, ReadTemp> {\n\n  Temp temp;\n  enum { OffsetType, ShadowType } type;\n\n  Offset offset;\n  Shadow shadow;\n\npublic:\n  /*! Read a temporary value in the data block at the specified offset.\n   *\n   * @param[in] temp      A temporary to store the shadow value.\n   * @param[in] offset    The offset in the data block where the temporary\n   *                      will be read.\n   */\n  ReadTemp(Temp temp, Offset offset)\n      : temp(temp), type(OffsetType), offset(offset), shadow(0) {}\n\n  /*! Read a temporary value from the last shadow with the same tag for this\n   * instruction.\n   *\n   * @param[in] temp      A temporary to store the shadow value.\n   * @param[in] shadow    The shadow in the data block where the temporary\n   *                      will be read.\n   */\n  ReadTemp(Temp temp, Shadow shadow)\n      : temp(temp), type(ShadowType), offset(0), shadow(shadow) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 DataBlock[offset]\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass WriteTemp : public AutoClone<PatchGenerator, WriteTemp> {\n\n  Temp temp;\n  Offset offset;\n  Shadow shadow;\n  Operand operand;\n  enum { OffsetType, ShadowType, OperandType } type;\n\npublic:\n  /*! Write a temporary value in the data block at the specified offset. This\n   * can be used to overwrite register values in the context part of the data\n   * block.\n   *\n   * @param[in] temp    A temporary which will be written.\n   *                    The value of the temporary is unchanged.\n   * @param[in] offset  The offset in the data block where the temporary\n   *                    will be written.\n   */\n  WriteTemp(Temp temp, Offset offset)\n      : temp(temp), offset(offset), shadow(0), operand(0), type(OffsetType) {}\n\n  /*! Write a temporary value in a shadow in the data block.\n   *\n   * @param[in] temp      A temporary which will be written.\n   *                      The value of the temporary is unchanged.\n   * @param[in] shadow    The shadow use to store the value.\n   */\n  WriteTemp(Temp temp, Shadow shadow)\n      : temp(temp), offset(0), shadow(shadow), operand(0), type(ShadowType) {}\n\n  /*! Write a temporary value another register (based on a operand)\n   *\n   * @param[in] temp      A temporary which will be written.\n   *                      The value of the temporary is unchanged.\n   * @param[in] operand   The shadow use to store the value.\n   */\n  WriteTemp(Temp temp, Operand operand)\n      : temp(temp), offset(0), shadow(0), operand(operand), type(OperandType) {}\n\n  /*! Output:\n   *\n   * MOV MEM64 DataBlock[offset], REG64 temp\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  inline bool modifyPC() const override {\n    return offset == Offset(Reg(REG_PC));\n  }\n};\n\nclass LoadReg : public PureEval<AutoClone<PatchGenerator, LoadReg>> {\n\n  Reg reg;\n  Offset offset;\n\npublic:\n  /*! Load a register from the data block at the specified offset. This can be\n   * used to load register values from the context part of the data block.\n   *\n   * @param[in] reg     A register where the value will be loaded.\n   * @param[in] offset  The offset in the data block from where the value will\n   * be loaded.\n   */\n  LoadReg(Reg reg, Offset offset) : reg(reg), offset(offset) {}\n\n  /*! Output:\n   *\n   * MOV REG64 reg, MEM64 DataBlock[offset]\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n};\n\nclass SaveTemp : public AutoClone<PatchGenerator, SaveTemp> {\n  Temp temp;\n  bool checkManager;\n\npublic:\n  /*! Save the value of the temporary register in the datablock.\n   * This can be used when an instruction can change the value of the register\n   * used as a temp\n   *\n   * @param[in] temp              A temp to save.\n   * @param[in] checkTmpManager   Only save if the register is saved by the\n   *                              register manager\n   */\n  SaveTemp(Temp temp, bool checkTmpManager)\n      : temp(temp), checkManager(checkTmpManager) {}\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass SaveReg : public PureEval<AutoClone<PatchGenerator, SaveReg>> {\n\n  Reg reg;\n  Offset offset;\n\npublic:\n  /*! Save a register in the data block at the specified offset. This can be\n   * used to save register values in the context part of the data block.\n   *\n   * @param[in] reg     A register which will be saved.\n   * @param[in] offset  The offset in the data block where the register will be\n   * written.\n   */\n  SaveReg(Reg reg, Offset offset) : reg(reg), offset(offset) {}\n\n  /*! Output:\n   *\n   * MOV MEM64 DataBlock[offset], REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n\n  inline bool modifyPC() const override {\n    return offset == Offset(Reg(REG_PC));\n  }\n};\n\nclass CopyReg : public AutoClone<PatchGenerator, CopyReg> {\n  Reg src;\n  Reg destReg;\n  Temp destTemp;\n\n  enum {\n    Reg2Temp,\n    Reg2Reg,\n  } type;\n\npublic:\n  /*! Copy a register in a temporary.\n   *\n   * @param[in] dest   A temporary where the register will be copied.\n   * @param[in] src    The register which will be copied\n   */\n  CopyReg(Temp dest, Reg src)\n      : src(src), destReg(0), destTemp(dest), type(Reg2Temp) {}\n\n  /*! Copy a register in a register.\n   *\n   * @param[in] src    The register which will be copied\n   * @param[in] dest   A register where the register will be copied.\n   */\n  CopyReg(Reg dest, Reg src)\n      : src(src), destReg(dest), destTemp(0), type(Reg2Reg) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass CopyTemp : public AutoClone<PatchGenerator, CopyTemp> {\n  Temp src;\n  Temp destTemp;\n  Reg destReg;\n\n  enum {\n    Temp2Temp,\n    Temp2Reg,\n  } type;\n\npublic:\n  /*! Copy a temporary in a temporary.\n   *\n   * @param[in] src    The temporary which will be copied\n   * @param[in] dest   A temporary where the temporary will be copied.\n   */\n  CopyTemp(Temp dest, Temp src)\n      : src(src), destTemp(dest), destReg(0), type(Temp2Temp) {}\n\n  /*! Copy a temporary in a register.\n   *\n   * @param[in] src    The temporary which will be copied\n   * @param[in] dest   A register where the temporary will be copied.\n   */\n  CopyTemp(Reg dest, Temp src)\n      : src(src), destTemp(0), destReg(dest), type(Temp2Reg) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, REG64 reg\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetInstId : public AutoClone<PatchGenerator, GetInstId> {\n\n  Temp temp;\n\npublic:\n  /*! Copy an ExecBlock specific id for the current instruction in a temporary.\n   * This id is used to identify the instruction responsible for a callback in\n   * the engine and is only meant for internal use.\n   *\n   * @param[in] temp   A temporary where the id will be copied.\n   */\n  GetInstId(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, IMM64 instID\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\n// Generic PatchGenerator that must be implemented by each target\n\nclass TargetPrologue : public AutoClone<PatchGenerator, TargetPrologue> {\n\npublic:\n  /*! Generate a label where the prologue can jump\n   */\n  TargetPrologue() {}\n\n  /*! Output:\n   *\n   * label\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const Patch &patch) const;\n};\n\nclass JmpEpilogue : public PureEval<AutoClone<PatchGenerator, JmpEpilogue>> {\n\npublic:\n  /*! Generate a jump instruction which target the epilogue of the ExecBlock.\n   */\n  JmpEpilogue() {}\n\n  /*! Output:\n   *\n   * JMP Offset(Epilogue)\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  genReloc(const LLVMCPU &llvmcpu) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/PatchRule.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <map>\n#include <utility>\n\n#include \"QBDI/Bitmask.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchRule.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nPatchRule::PatchRule(PatchCondition::UniquePtr &&condition,\n                     std::vector<std::unique_ptr<PatchGenerator>> &&generators)\n    : condition(std::move(condition)), generators(std::move(generators)) {};\n\nPatchRule::~PatchRule() = default;\n\nPatchRule::PatchRule(PatchRule &&) = default;\n\nbool PatchRule::canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const {\n  return condition->test(patch, llvmcpu);\n}\n\nvoid PatchRule::apply(Patch &patch, const LLVMCPU &llvmcpu) const {\n\n  TempManager temp_manager(patch);\n  bool modifyPC = false;\n\n  patch.patchGenFlags.emplace_back(patch.insts.size(),\n                                   PatchGeneratorFlags::PatchRuleBegin);\n  for (const auto &g : generators) {\n    if (g->getPreFlags() != PatchGeneratorFlags::None) {\n      patch.patchGenFlags.emplace_back(patch.insts.size(), g->getPreFlags());\n    }\n    patch.append(g->generate(patch, temp_manager));\n    if (g->getPostFlags() != PatchGeneratorFlags::None) {\n      patch.patchGenFlags.emplace_back(patch.insts.size(), g->getPostFlags());\n    }\n    modifyPC |= g->modifyPC();\n  }\n  patch.patchGenFlags.emplace_back(patch.insts.size(),\n                                   PatchGeneratorFlags::PatchRuleEnd);\n  patch.setModifyPC(modifyPC);\n\n  RelocatableInst::UniquePtrVec saveReg, restoreReg;\n  Reg::Vec unrestoredReg;\n\n  temp_manager.generateSaveRestoreInstructions(0, saveReg, restoreReg,\n                                               unrestoredReg);\n  patch.prepend(std::move(saveReg));\n  patch.append(std::move(restoreReg));\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/PatchRule.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHRULE_H\n#define PATCHRULE_H\n\n#include <memory>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/Patch.h\"\n\nnamespace llvm {\nclass MCInst;\n} // namespace llvm\n\nnamespace QBDI {\nclass LLVMCPU;\nclass PatchGenerator;\nclass PatchCondition;\nclass RelocatableInst;\n\n/*! A patch rule written in PatchDSL.\n */\nclass PatchRule {\n  std::unique_ptr<PatchCondition> condition;\n  std::vector<std::unique_ptr<PatchGenerator>> generators;\n\npublic:\n  /*! Allocate a new patch rule with a condition and a list of generators.\n   *\n   * @param[in] condition   A PatchCondition which determine wheter or not this\n   *                        PatchRule applies.\n   * @param[in] generators  A vector of PatchGenerator which will produce the\n   *                        patch instructions.\n   */\n  PatchRule(std::unique_ptr<PatchCondition> &&condition,\n            std::vector<std::unique_ptr<PatchGenerator>> &&generators);\n\n  PatchRule(PatchRule &&);\n\n  ~PatchRule();\n\n  /*! Determine wheter this rule applies by evaluating this rule condition on\n   * the current context.\n   *\n   * @param[in] patch     The Patch to check\n   * @param[in] llvmcpu   LLVMCPU object\n   *\n   * @return True if this patch condition evaluate to true on this context.\n   */\n  bool canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const;\n\n  /*! Generate this rule output patch by evaluating its generators on the\n   * current context. Also handles the temporary register management for this\n   * patch.\n   *\n   * @param[in] patch     The Patch where to apply the rule\n   * @param[in] llvmcpu   LLVMCPU object\n   */\n  void apply(Patch &patch, const LLVMCPU &llvmcpu) const;\n};\n\n} // namespace QBDI\n\n#endif // PATCHRULE_H\n"
  },
  {
    "path": "src/Patch/PatchRuleAssembly.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHRULEASSEMBLY_H\n#define PATCHRULEASSEMBLY_H\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n#include \"Patch/X86_64/PatchRuleAssembly_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"Patch/ARM/PatchRuleAssembly_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"Patch/AARCH64/PatchRuleAssembly_AARCH64.h\"\n#endif\n\n#endif // PATCHRULEASSEMBLY_H\n"
  },
  {
    "path": "src/Patch/PatchRuleAssemblyBase.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHRULEASSEMBLYBASE_H\n#define PATCHRULEASSEMBLYBASE_H\n\n#include <memory>\n#include <vector>\n\n#include \"QBDI/Options.h\"\n#include \"QBDI/State.h\"\n\nnamespace llvm {\nclass MCInst;\n} // namespace llvm\n\nnamespace QBDI {\nclass LLVMCPU;\nclass Patch;\n\n/*! The patchRule allows QBDI to apply property for an instruction to the next\n *  one (in the same basicblock).\n */\nclass PatchRuleAssemblyBase {\npublic:\n  /*! Change QBDI options associated with the VM\n   *\n   * @param[in] opts  The new options\n   *\n   * @return  True if the whole cache should be invalidated\n   */\n  virtual bool changeOptions(Options opts) = 0;\n\n  /*! Generate a patch for this MCInst\n   *\n   * @param[in] inst      The instruction to instrument\n   * @param[in] address   The address of the instruction\n   * @param[in] instSize  The size of the instruction\n   * @param[in] llvmcpu   The CPU used for this instruction. The same CPU is\n   *                      used for all instruction in the same BB\n   * @param[in] patchList The list of patch generated\n   *\n   * @return  True if this instruction is the last of a basicBlock\n   */\n  virtual bool generate(const llvm::MCInst &inst, rword address,\n                        uint32_t instSize, const LLVMCPU &llvmcpu,\n                        std::vector<Patch> &patchList) = 0;\n\n  /*! Clean patchList if the basicBlock should be end early (ie: if error in the\n   *  in the next instruction or read unmapped memory). This flush the pending\n   *  patch and remove unfinish Patch from patchList\n   *\n   * @param[in] llvmcpu   The CPU used for this instruction. The same CPU is\n   *                      used for all instruction in the same BB\n   * @param[in] patchList The list of patch generated\n   *\n   * @return  True if the operation success. Otherwise, patchList should not be\n   *          used\n   */\n  virtual bool earlyEnd(const LLVMCPU &llvmcpu,\n                        std::vector<Patch> &patchList) = 0;\n};\n\n} // namespace QBDI\n\n#endif // PATCHRULEASSEMBLYBASE_H\n"
  },
  {
    "path": "src/Patch/PatchUtils.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHUTILS_H\n#define PATCHUTILS_H\n\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <utility>\n#include <vector>\n\nnamespace QBDI {\n\n// helper to create a object as a unique_ptr\ntemplate <typename T, typename U>\nclass AutoUnique : public T {\npublic:\n  template <typename... Args>\n  AutoUnique(Args &&...args) : T(std::forward<Args>(args)...) {}\n\n  template <typename... Args>\n  inline static std::unique_ptr<T> unique(Args &&...args) {\n    return std::make_unique<U>(std::forward<Args>(args)...);\n  };\n};\n\n// helper to clone object, when the copy is possible\ntemplate <typename T, typename U>\nclass AutoClone : public AutoUnique<T, U> {\npublic:\n  template <typename... Args>\n  AutoClone(Args &&...args) : AutoUnique<T, U>(std::forward<Args>(args)...) {}\n\n  inline std::unique_ptr<T> clone() const override {\n    return std::make_unique<U>(*static_cast<const U *>(this));\n  };\n};\n\ntemplate <class T>\ninline void _conv_unique(std::vector<std::unique_ptr<T>> &vec,\n                         std::unique_ptr<T> &&u) {\n  vec.push_back(std::forward<std::unique_ptr<T>>(u));\n}\n\ntemplate <class T, class... Args>\ninline void _conv_unique(std::vector<std::unique_ptr<T>> &vec,\n                         std::unique_ptr<T> &&u, Args... args) {\n  vec.push_back(std::forward<std::unique_ptr<T>>(u));\n  _conv_unique<T>(vec, std::forward<Args>(args)...);\n}\n\ntemplate <class T, class... Args>\ninline std::vector<std::unique_ptr<T>> conv_unique(Args... args) {\n  std::vector<std::unique_ptr<T>> vec;\n  _conv_unique<T>(vec, std::forward<Args>(args)...);\n  return vec;\n}\n\ntemplate <class T>\ninline std::vector<std::unique_ptr<T>>\ncloneVec(const std::vector<std::unique_ptr<T>> &u) {\n  std::vector<std::unique_ptr<T>> v;\n  std::transform(u.cbegin(), u.cend(), std::back_inserter(v),\n                 [](const std::unique_ptr<T> &c) {\n                   if (c)\n                     return c->clone();\n                   return std::unique_ptr<T>();\n                 });\n  return v;\n}\n\ntemplate <class T>\ninline void append(std::vector<std::unique_ptr<T>> &u,\n                   std::vector<std::unique_ptr<T>> v) {\n  std::move(v.begin(), v.end(), std::back_inserter(u));\n}\n\ntemplate <class T>\ninline void prepend(std::vector<std::unique_ptr<T>> &u,\n                    std::vector<std::unique_ptr<T>> v) {\n  std::move(u.begin(), u.end(), std::back_inserter(v));\n  u.swap(v);\n}\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/Register.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <sstream>\n#include <utility>\n\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n\n#include \"devVariable.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n\n#include \"Utility/LogSys.h\"\n\n#if DEBUG_INST_OPERAND\n#define DEBUG_REGISTER QBDI_DEBUG\n#else\n#define DEBUG_REGISTER(...)\n#endif\n\nnamespace QBDI {\n\nstatic void addRegisterInfo(std::array<RegisterUsage, NUM_GPR> &regArr,\n                            std::map<RegLLVM, RegisterUsage> &regMap,\n                            RegLLVM reg_, RegisterUsage usage) {\n\n  for (int i = 0; i < getRegisterPacked(reg_); i++) {\n\n    RegLLVM reg = getUpperRegister(reg_, i);\n    int id = getGPRPosition(reg);\n    if (id < 0 or ((unsigned)id) >= NUM_GPR) {\n      auto e = regMap.find(reg);\n      if (e != regMap.end()) {\n        regMap[reg] = usage | e->second;\n      } else {\n        regMap[reg] = usage;\n      }\n    } else {\n      regArr[id] |= usage;\n    }\n  }\n}\n\nvoid getUsedGPR(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                std::array<RegisterUsage, NUM_GPR> &regUsage,\n                std::map<RegLLVM, RegisterUsage> &regUsageExtra) {\n\n  const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n  const llvm::MCInstrDesc &desc = MCII.get(inst.getOpcode());\n  unsigned opIsUsedBegin = desc.getNumDefs();\n  unsigned opIsUsedEnd = inst.getNumOperands();\n  if (desc.isVariadic() and variadicOpsIsWrite(inst)) {\n    if (desc.getNumOperands() < 1) {\n      opIsUsedEnd = 0;\n    } else {\n      opIsUsedEnd = desc.getNumOperands() - 1;\n    }\n  }\n  DEBUG_REGISTER(\n      \"Opcode : {}, Variadic : {}, opIsUsedBegin : {}, opIsUsedEnd : {}\",\n      llvmcpu.getInstOpcodeName(inst.getOpcode()), desc.isVariadic(),\n      opIsUsedBegin, opIsUsedEnd);\n\n  for (unsigned int i = 0; i < inst.getNumOperands(); i++) {\n    const llvm::MCOperand &op = inst.getOperand(i);\n    if (op.isReg()) {\n      RegLLVM regNum = op.getReg();\n      if (regNum == /* llvm::X86|AArch64::NoRegister */ 0) {\n        DEBUG_REGISTER(\"{} Reg {}\", i, regNum.getValue());\n        continue;\n      }\n      if (i < opIsUsedBegin or opIsUsedEnd <= i) {\n        DEBUG_REGISTER(\"{} Reg Set {}\", i, llvmcpu.getRegisterName(regNum));\n        addRegisterInfo(regUsage, regUsageExtra, regNum, RegisterSet);\n      } else {\n        DEBUG_REGISTER(\"{} Reg Used {}\", i, llvmcpu.getRegisterName(regNum));\n        addRegisterInfo(regUsage, regUsageExtra, regNum, RegisterUsed);\n      }\n    } else if (op.isImm()) {\n      DEBUG_REGISTER(\"{} Imm 0x{:x}\", i, op.getImm());\n    } else {\n      DEBUG_REGISTER(\"{} Unknown\", i);\n    }\n  }\n\n  for (const unsigned implicitRegs : desc.implicit_uses()) {\n    if (implicitRegs) {\n      DEBUG_REGISTER(\"Reg ImplicitUses {}\",\n                     llvmcpu.getRegisterName(implicitRegs));\n      addRegisterInfo(regUsage, regUsageExtra, implicitRegs, RegisterUsed);\n    }\n  }\n\n  for (const unsigned implicitRegs : desc.implicit_defs()) {\n    if (implicitRegs) {\n      DEBUG_REGISTER(\"Reg ImplicitDefs {}\",\n                     llvmcpu.getRegisterName(implicitRegs));\n      addRegisterInfo(regUsage, regUsageExtra, implicitRegs, RegisterSet);\n    }\n  }\n\n  fixLLVMUsedGPR(inst, llvmcpu, regUsage, regUsageExtra);\n\n  QBDI_DEBUG_BLOCK({\n    std::ostringstream oss;\n    bool first = true;\n    for (unsigned i = 0; i < NUM_GPR; i++) {\n      if (regUsage[i] == 0) {\n        continue;\n      }\n      if (not first) {\n        oss << \", \";\n      } else {\n        first = false;\n      }\n      oss << llvmcpu.getRegisterName(GPR_ID[i]);\n      switch (regUsage[i] & RegisterBoth) {\n        default:\n          oss << \" (--)\";\n          break;\n        case RegisterUsed:\n          oss << \" (r-)\";\n          break;\n        case RegisterSet:\n          oss << \" (-w)\";\n          break;\n        case RegisterBoth:\n          oss << \" (rw)\";\n          break;\n      }\n    }\n    for (const auto &e : regUsageExtra) {\n      if (not first) {\n        oss << \", \";\n      } else {\n        first = false;\n      }\n      oss << llvmcpu.getRegisterName(e.first);\n      switch (e.second & RegisterBoth) {\n        default:\n          oss << \" (--)\";\n          break;\n        case RegisterUsed:\n          oss << \" (r-)\";\n          break;\n        case RegisterSet:\n          oss << \" (-w)\";\n          break;\n        case RegisterBoth:\n          oss << \" (rw)\";\n          break;\n      }\n    }\n    DEBUG_REGISTER(\"Found Register Usage : {}\", oss.str());\n  });\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/Register.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef REGISTER_H\n#define REGISTER_H\n\n#include <array>\n#include <cstddef>\n#include <map>\n#include <stdint.h>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/State.h\"\n\nnamespace llvm {\nclass MCInst;\n} // namespace llvm\n\nnamespace QBDI {\nclass LLVMCPU;\nclass Patch;\nstruct RegLLVM;\n\nextern const RegLLVM GPR_ID[];\nextern const RegLLVM FLAG_ID[];\nextern const RegLLVM SEG_ID[];\nextern const std::map<RegLLVM, int16_t> FPR_ID;\nextern const unsigned int size_GPR_ID;\nextern const unsigned int size_FLAG_ID;\nextern const unsigned int size_SEG_ID;\n\n// Get register size\nuint8_t getRegisterSize(RegLLVM reg);\n// Get register offset from base register (case of AH)\nuint8_t getRegisterBaseOffset(RegLLVM reg);\n// Get the number of real register in the LLVM register\nuint8_t getRegisterPacked(RegLLVM reg);\n// If packed register, get the space between to packed register\nuint8_t getRegisterSpaced(RegLLVM reg);\n// Get the position of a LLVM register in GPRState\nsize_t getGPRPosition(RegLLVM reg);\n// Get the full size register (AX => RAX for example in x86_64)\nRegLLVM getUpperRegister(RegLLVM reg, size_t pos = 0);\n// Get the packed register (R0_R1 pos 1 => R1 for example in ARM)\nRegLLVM getPackedRegister(RegLLVM reg, size_t pos = 0);\n\nenum RegisterUsage : uint8_t {\n  RegisterUnused = 0,\n  RegisterUsed = 0x1,\n  RegisterSet = 0x2,\n  RegisterBoth = 0x3,\n  // RegisterSaved means the instrumentation will load this register from\n  // the GPRState if used by the tempManager\n  RegisterSaved = 0x4,\n  // RegisterSaved means the instrumentation will load this register from\n  // the GPRState if used as a Scratch Register\n  RegisterSavedScratch = 0x8\n};\n\n_QBDI_ENABLE_BITMASK_OPERATORS(RegisterUsage)\n\n/* Add register not declared by llvm in the RegisterUsage\n *\n * This method is called by getUsedGPR and must be implemented by each target\n * to fix missing declaration of LLVM.\n */\nvoid fixLLVMUsedGPR(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                    std::array<RegisterUsage, NUM_GPR> &,\n                    std::map<RegLLVM, RegisterUsage> &);\n\n/* Get General Register used and set by an instruction (needed for TempManager)\n *\n * If a GPR is not in the map, the instruction doesn't not used/set the register\n *\n * /!\\ LLVM may not include all usage of stack register (mostly on call/ret\n * instruction)\n */\nvoid getUsedGPR(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                std::array<RegisterUsage, NUM_GPR> &regUsage,\n                std::map<RegLLVM, RegisterUsage> &regUsageExtra);\n\n}; // namespace QBDI\n\n#endif // REGISTER_H\n"
  },
  {
    "path": "src/Patch/RelocatableInst.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef RELOCATABLEINST_H\n#define RELOCATABLEINST_H\n\n#include <memory>\n#include <vector>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"QBDI/State.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass ExecBlock;\nclass LLVMCPU;\n\nclass RelocatableInst {\npublic:\n  using UniquePtr = std::unique_ptr<RelocatableInst>;\n  using UniquePtrVec = std::vector<std::unique_ptr<RelocatableInst>>;\n\n  RelocatableInst() {}\n\n  virtual RelocatableInstTag getTag() const { return RelocInst; };\n\n  virtual std::unique_ptr<RelocatableInst> clone() const = 0;\n\n  virtual int getSize(const LLVMCPU &llvmcpu) const = 0;\n\n  virtual llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const = 0;\n\n  virtual ~RelocatableInst() = default;\n};\n\nstatic inline int getUniquePtrVecSize(const RelocatableInst::UniquePtrVec &vec,\n                                      const LLVMCPU &llvmcpu) {\n  int size = 0;\n  for (const auto &r : vec) {\n    size += r->getSize(llvmcpu);\n  }\n  return size;\n}\n\nclass NoReloc : public AutoClone<RelocatableInst, NoReloc> {\n  llvm::MCInst inst;\n\npublic:\n  NoReloc(llvm::MCInst &&inst) : inst(std::forward<llvm::MCInst>(inst)) {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override {\n    return inst;\n  }\n\n  int getSize(const LLVMCPU &llvmcpu) const override {\n    return getInstSize(inst, llvmcpu);\n  };\n};\n\n// Generic RelocatableInst that must be implemented by each target\n\nclass RelocTag : public AutoClone<RelocatableInst, RelocTag> {\n  RelocatableInstTag tag;\n\npublic:\n  RelocTag(RelocatableInstTag t) : tag(t) {}\n\n  RelocatableInstTag getTag() const override { return tag; }\n\n  // The Execblock must skip the generation if a RelocatableInst doesn't return\n  // RelocInst. Generate a NOP and a log Error.\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return 0; }\n};\n\nclass LoadShadow : public AutoClone<RelocatableInst, LoadShadow> {\n  RegLLVM reg;\n  uint16_t tag;\n\npublic:\n  LoadShadow(RegLLVM reg, Shadow tag)\n      : AutoClone<RelocatableInst, LoadShadow>(), reg(reg), tag(tag.getTag()) {}\n\n  // Load a value from the last shadow with the given tag\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass StoreShadow : public AutoClone<RelocatableInst, StoreShadow> {\n  RegLLVM reg;\n  uint16_t tag;\n  bool create;\n\npublic:\n  StoreShadow(RegLLVM reg, Shadow tag, bool create)\n      : AutoClone<RelocatableInst, StoreShadow>(), reg(reg), tag(tag.getTag()),\n        create(create) {}\n\n  // Store a value to a shadow\n  // if create, the shadow is create in the ExecBlock with the given tag\n  // otherwise, the last shadow with this tag is used\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass LoadDataBlock : public AutoClone<RelocatableInst, LoadDataBlock> {\n  RegLLVM reg;\n  int64_t offset;\n\npublic:\n  LoadDataBlock(RegLLVM reg, int64_t offset)\n      : AutoClone<RelocatableInst, LoadDataBlock>(), reg(reg), offset(offset) {}\n\n  // Load a value from the specified offset of the datablock\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass StoreDataBlock : public AutoClone<RelocatableInst, StoreDataBlock> {\n  RegLLVM reg;\n  int64_t offset;\n\npublic:\n  StoreDataBlock(RegLLVM reg, int64_t offset)\n      : AutoClone<RelocatableInst, StoreDataBlock>(), reg(reg), offset(offset) {\n  }\n\n  // Store a value to the specified offset of the datablock\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass MovReg : public AutoClone<RelocatableInst, MovReg> {\n  RegLLVM dst;\n  RegLLVM src;\n\npublic:\n  MovReg(RegLLVM dst, RegLLVM src)\n      : AutoClone<RelocatableInst, MovReg>(), dst(dst), src(src) {}\n\n  // Move a value from a register to another\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass LoadImm : public AutoClone<RelocatableInst, LoadImm> {\n  RegLLVM reg;\n  Constant imm;\n\npublic:\n  LoadImm(RegLLVM reg, Constant imm)\n      : AutoClone<RelocatableInst, LoadImm>(), reg(reg), imm(imm) {}\n\n  // Set the register to this value\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass InstId : public AutoClone<RelocatableInst, InstId> {\n  RegLLVM reg;\n\npublic:\n  InstId(RegLLVM reg) : AutoClone<RelocatableInst, InstId>(), reg(reg) {}\n\n  // Store the current instruction ID in the register\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/TempManager.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <memory>\n#include <stdlib.h>\n\n#include \"llvm/MC/MCRegister.h\"     // for MCRegister\n#include \"llvm/MC/MCRegisterInfo.h\" // for MCRegisterInfo\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n#include \"Patch/X86_64/TempManagerImpl_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"Patch/ARM/TempManagerImpl_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"Patch/AARCH64/TempManagerImpl_AARCH64.h\"\n#endif\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n// skip RAX as it is very often used implicitly and LLVM\n// sometimes don't tell us...\nstatic constexpr unsigned int _QBDI_FIRST_FREE_REGISTER = 1;\n#else\nstatic constexpr unsigned int _QBDI_FIRST_FREE_REGISTER = 0;\n#endif\n\nnamespace QBDI {\n\nTempManager::TempManager(Patch &patch)\n    : patch(patch), MRI(patch.llvmcpu->getMRI()), usedRegisterBitField(0),\n      lockNewTmpReg(true) {}\n\nbool TempManager::usedRegister(Reg reg) const {\n  return (usedRegisterBitField & (((rword)1) << reg.getID())) != 0;\n}\n\nbool TempManager::isAllocatedId(unsigned int id) const {\n  for (const auto &p : temps) {\n    if (p.first == id) {\n      return true;\n    }\n  }\n  return false;\n}\n\nvoid TempManager::associatedReg(unsigned int id, Reg reg) {\n  QBDI_REQUIRE_ABORT(\n      lockNewTmpReg,\n      \"Cannot allocate new TempRegister after lockTempManager()\");\n\n  QBDI_REQUIRE_ABORT(not usedRegister(reg),\n                     \"Cannot associate twice the same register\");\n  QBDI_REQUIRE_ABORT(not isAllocatedId(id),\n                     \"Cannot reassociate an existing register\");\n\n  temps.emplace_back(id, reg);\n  patch.tempReg.insert(reg);\n  usedRegisterBitField |= (((rword)1) << reg.getID());\n}\n\nReg TempManager::getRegForTemp(unsigned int id) {\n\n  // Check if the id is already alocated\n  for (const auto &p : temps) {\n    if (p.first == id) {\n      return p.second;\n    }\n  }\n\n  QBDI_REQUIRE_ABORT(\n      lockNewTmpReg,\n      \"Cannot allocate new TempRegister after lockTempManager()\");\n\n  // try to find a register that doesn't need to be restore\n  for (const auto &r : TempManagerUnrestoreGPR) {\n    if ((not usedRegister(r)) and patch.regUsage[r.getID()] == 0) {\n      associatedReg(id, r);\n      return r;\n    }\n  }\n\n  // Find a free register\n  for (unsigned i = _QBDI_FIRST_FREE_REGISTER; i < AVAILABLE_GPR; i++) {\n    Reg r = Reg(i);\n    if ((not usedRegister(r)) and patch.regUsage[i] == 0) {\n      // store it and return it\n      associatedReg(id, r);\n      return r;\n    }\n  }\n\n  // Find a register with RegisterUsage::RegisterSaved\n  for (unsigned i = _QBDI_FIRST_FREE_REGISTER; i < AVAILABLE_GPR; i++) {\n    Reg r = Reg(i);\n    if ((not usedRegister(r)) and patch.regUsage[i] != 0 and\n        (patch.regUsage[i] & RegisterUsage::RegisterSaved) != 0 and\n        (patch.regUsage[i] & RegisterUsage::RegisterSavedScratch) == 0) {\n      // store it and return it\n      associatedReg(id, r);\n      return r;\n    }\n  }\n\n  QBDI_CRITICAL(\"No free registers found\");\n  QBDI_CRITICAL(\"current tmp reg :\");\n  for (const auto &p : temps) {\n    QBDI_CRITICAL(\"- Temp({}) = GPR_ID[{}]\", p.first, p.second.getID());\n  }\n  QBDI_ABORT(\"need Temp({})\", id);\n}\n\nReg::Vec TempManager::getUsedRegisters() const {\n  Reg::Vec list;\n  for (auto &p : temps) {\n    list.push_back(p.second);\n  }\n  return list;\n}\n\nsize_t TempManager::getUsedRegisterNumber() const { return temps.size(); }\n\nbool TempManager::shouldRestore(Reg r) const {\n  return TempManagerUnrestoreGPR.count(r) == 0;\n}\n\nRegLLVM TempManager::getSizedSubReg(RegLLVM reg, unsigned size) const {\n  if (getRegisterSize(reg) == size) {\n    return reg;\n  }\n  for (unsigned i = 1; i < MRI.getNumSubRegIndices(); i++) {\n    RegLLVM subreg = MRI.getSubReg(reg.getValue(), i).id();\n    if (subreg != 0 && getRegisterSize(subreg) == size) {\n      return subreg;\n    }\n  }\n  QBDI_ABORT(\"No sub register of size {} found for register {} ({})\", size,\n             reg.getValue(), MRI.getName(reg.getValue()));\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/TempManager.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef TEMPMANAGER_H\n#define TEMPMANAGER_H\n\n#include <memory>\n#include <set>\n#include <stddef.h>\n#include <utility>\n#include <vector>\n\n#include \"Patch/Types.h\"\n\nnamespace llvm {\nclass MCInstrInfo;\nclass MCRegisterInfo;\n} // namespace llvm\n\nnamespace QBDI {\nclass LLVMCPU;\nclass Patch;\nclass RelocatableInst;\n\n// Helper classes\n\nclass TempManager {\n\n  Patch &patch;\n  const llvm::MCRegisterInfo &MRI;\n\n  // list of TmpRegister\n  std::vector<std::pair<unsigned int, Reg>> temps;\n  // bitfield of UsedTempReg\n  rword usedRegisterBitField;\n  // allowed to register a new TmpRegister\n  bool lockNewTmpReg;\n\npublic:\n  TempManager(Patch &patch);\n\n  Reg getRegForTemp(unsigned int id);\n\n  // allocate a new TmpRegiter\n  void associatedReg(unsigned int id, Reg reg);\n\n  Reg::Vec getUsedRegisters() const;\n\n  void lockTempManager() { lockNewTmpReg = false; }\n\n  bool shouldRestore(Reg r) const;\n\n  bool usedRegister(Reg reg) const;\n\n  bool isAllocatedId(unsigned int id) const;\n\n  size_t getUsedRegisterNumber() const;\n\n  RegLLVM getSizedSubReg(RegLLVM reg, unsigned size) const;\n\n  const Patch &getPatch() const { return patch; };\n\n  void generateSaveRestoreInstructions(\n      unsigned unrestoredRegNum,\n      std::vector<std::unique_ptr<RelocatableInst>> &saveInst,\n      std::vector<std::unique_ptr<RelocatableInst>> &restoreInst,\n      Reg::Vec &unrestoredReg) const;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/Types.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef TYPES_H\n#define TYPES_H\n\n#include <vector>\n\n#include \"ExecBlock/Context.h\"\n#include \"Patch/Register.h\"\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\n/*! Structure representing a register in LLVM.\n *\n * The goals of this structure is to force the manipulation of the\n * integer llvm id by the method getValue().\n *\n * With this method, the compiler cannot not longuer perform the followed\n * implicit conversion:\n *  - Reg((unsigned) Reg(x) )\n */\nstruct RegLLVM {\n  unsigned int id;\n\npublic:\n  /*! Create a new register variable.\n   *\n   *  @param[in] id The llvm id of the register to represent.\n   */\n  inline RegLLVM(unsigned int id = 0) : id(id) {};\n\n  /*! Get the llvm value of the register\n   *\n   * @return llvm register id.\n   */\n  inline unsigned int getValue() const { return id; }\n\n  inline bool operator==(const RegLLVM &o) const { return id == o.id; }\n\n  inline bool operator!=(const RegLLVM &o) const { return id != o.id; }\n\n  /*! Needed to create a std::map\n   */\n  inline bool operator<(const RegLLVM &o) const { return id < o.id; }\n};\n\ninline bool operator==(unsigned int id, const RegLLVM &reg) {\n  return reg.getValue() == id;\n}\n\ninline bool operator!=(unsigned int id, const RegLLVM &reg) {\n  return reg.getValue() != id;\n}\n\n/*! Structure representing a register variable in PatchDSL.\n */\nstruct Reg {\n  unsigned int id;\n\npublic:\n  using Vec = std::vector<Reg>;\n\n  /*! Create a new register variable.\n   *\n   *  @param[in] id The id of the register to represent.\n   */\n  inline Reg(unsigned int id) : id(id) {};\n\n  /*! Get back the id of the register in GPRState\n   *\n   * @return GPRState register id.\n   */\n  inline unsigned int getID() const { return id; }\n\n  /*! Convert this structure to an LLVM register id.\n   *\n   * @return LLVM register id.\n   */\n  inline operator RegLLVM() const { return GPR_ID[id]; }\n\n  /*! Get the llvm value of the register\n   *\n   * @return llvm register id.\n   */\n  inline unsigned int getValue() const { return GPR_ID[id].getValue(); }\n\n  /*! Return the offset of this register storage in the context part of the data\n   * block.\n   *\n   * @return The offset.\n   */\n  inline rword offset() const {\n    return offsetof(Context, gprState) + sizeof(rword) * id;\n  }\n\n  inline bool operator==(const RegLLVM &o) const { return o == *this; }\n\n  inline bool operator!=(const RegLLVM &o) const { return o != *this; }\n\n  /* Anbigious method: do we compare the index or the llvm integer ?\n   */\n  inline bool operator==(unsigned int id) const = delete;\n  inline bool operator!=(unsigned int id) const = delete;\n\n  /*! Needed to create a std::set\n   */\n  inline bool operator<(const Reg &o) const { return id < o.id; }\n};\n\n/*! Structure representing a shadow variable in PatchDSL.\n */\nstruct Shadow {\n\n  uint16_t tag;\n\npublic:\n  /*! Allocate a new shadow variable in the data block with the corresponding\n   * tag.\n   *\n   *  @param[in] tag The tag of the new shadow variable.\n   */\n  inline Shadow(uint16_t tag) : tag(tag) {}\n\n  /*! Return the tag associated with this shadow variable.\n   *\n   *  @return The tag of the shadow variable.\n   */\n  inline rword getTag() const { return tag; }\n};\n\nenum ShadowReservedTag : uint16_t {\n\n  // MemoryAccess Tag\n  MEMORY_TAG_BEGIN = 0xffe0,\n  MEMORY_TAG_END = 0xfff0,\n\n  // also defined in Callback.h\n  Untagged = 0xffff,\n};\n\n/*! Structure representing a constant value in PatchDSL.\n */\nstruct Constant {\n\n  rword v;\n\n  /*! Represent a constant value.\n   *\n   *  @param[in] v The represented value.\n   */\n  inline Constant(rword v) : v(v) {}\n\n  /*! Convert this structure to its value.\n   *\n   * @return This constant value.\n   */\n  inline operator rword() const { return v; }\n};\n\n/*! Structure representing a memory offset variable in PatchDSL.\n */\nstruct Offset {\n\n  int64_t offset;\n\npublic:\n  /*! Allocate a new offset variable with its offset value.\n   *\n   *  @param[in] offset The offset value\n   */\n  inline Offset(int64_t offset) : offset(offset) {}\n\n  /*! Allocate a new offset variable with the offset in the context of a\n   * specific register.\n   *\n   *  @param[in] reg The register whose offset to represent.\n   */\n  inline Offset(Reg reg) : offset(reg.offset()) {}\n\n  /*! Convert this structure to its value.\n   *\n   * @return This offset value.\n   */\n  inline operator int64_t() const { return offset; }\n};\n\n/*! Structure representing a temporary register variable in PatchDSL.\n */\nstruct Temp {\n\n  unsigned int id;\n\npublic:\n  /*! Represent a temporary register variable idenified by a unique ID. Inside a\n   * patch rules or a instrumentation rules, Temp with identical ids point to\n   * the same physical register. The id 0xFFFFFFFF is reserved for internal\n   * uses. The mapping from id to physical register is determined at generation\n   * time and the allocation and deallocation instructions are automatically\n   * added to the patch.\n   *\n   *  @param[in] id The id of the temp to represent.\n   */\n  inline Temp(unsigned int id) : id(id) {}\n\n  /*! Convert this Temp to its id.\n   *\n   * @return This Temp id.\n   */\n  inline operator unsigned int() const { return id; }\n};\n\n/*! Structure representing an operand instruction variable in PatchDSL.\n */\nstruct Operand {\n\n  unsigned int idx;\n\npublic:\n  /*! Represent an operand instruction identified by its index in the LLVM\n   * MCInst representation of the instruction.\n   *\n   *  @param[in] idx The operand index.\n   */\n  inline Operand(unsigned int idx) : idx(idx) {}\n\n  /*! Convert this Operand to its idx.\n   *\n   * @return This Operand idx.\n   */\n  inline operator unsigned int() const { return idx; }\n};\n\n/* Tag value for RelocatableInst\n */\nenum RelocatableInstTag {\n  RelocInst = 0,\n  RelocTagChangeScratchRegister = 0x1,\n  RelocTagPatchBegin = 0x10,\n  RelocTagPreInstMemAccess = 0x20,\n  RelocTagPreInstStdCBK = 0x21,\n  RelocTagPatchInstBegin = 0x30,\n  RelocTagPatchInstEnd = 0x31,\n  RelocTagPostInstMemAccess = 0x40,\n  RelocTagPostInstStdCBK = 0x41,\n  RelocTagInvalid = 0xff,\n};\n\n} // namespace QBDI\n\n#endif // TYPES_H\n"
  },
  {
    "path": "src/Patch/X86_64/CMakeLists.txt",
    "content": "set(SOURCES\n    \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockFlags_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockPatch_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstInfo_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/InstrRules_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Layer2_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccess_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchGenerator_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/PatchRuleAssembly_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/Register_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/RelocatableInst_X86_64.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/TempManager_X86_64.cpp\")\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Patch/X86_64/ExecBlockFlags_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"MCTargetDesc/X86BaseInfo.h\"\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/ExecBlockFlags.h\"\n#include \"Patch/Types.h\"\n#include \"Patch/X86_64/ExecBlockFlags_X86_64.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\nnamespace {\n\nstruct ExecBlockFlagsArray {\n  uint8_t arr[llvm::X86::NUM_TARGET_REGS];\n\n  constexpr ExecBlockFlagsArray() : arr() {\n    for (unsigned i = 0; i < llvm::X86::NUM_TARGET_REGS; i++) {\n      if (llvm::X86::YMM0 <= i && i <= llvm::X86::YMM15) {\n        arr[i] = ExecBlockFlags::needAVX | ExecBlockFlags::needFPU;\n      } else if ((llvm::X86::XMM0 <= i && i <= llvm::X86::XMM15) ||\n                 (llvm::X86::ST0 <= i && i <= llvm::X86::ST7) ||\n                 (llvm::X86::MM0 <= i && i <= llvm::X86::MM7) ||\n                 llvm::X86::FPSW == i || llvm::X86::FPCW == i) {\n        arr[i] = ExecBlockFlags::needFPU;\n      } else if (i == llvm::X86::FS || i == llvm::X86::GS) {\n        arr[i] = ExecBlockFlags::needFSGS;\n      } else {\n        arr[i] = 0;\n      }\n    }\n  }\n\n  inline uint8_t get(RegLLVM reg_) const {\n    size_t reg = reg_.getValue();\n    if (reg < llvm::X86::NUM_TARGET_REGS)\n      return arr[reg];\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return 0;\n  }\n};\n\n} // namespace\n\nconst uint8_t defaultExecuteFlags = ExecBlockFlags::needAVX |\n                                    ExecBlockFlags::needFPU |\n                                    ExecBlockFlags::needFSGS;\n\nuint8_t getExecBlockFlags(const llvm::MCInst &inst,\n                          const QBDI::LLVMCPU &llvmcpu) {\n  static constexpr ExecBlockFlagsArray cache;\n\n  const llvm::MCInstrDesc &desc = llvmcpu.getMCII().get(inst.getOpcode());\n  uint8_t flags = 0;\n\n  // register flag\n  for (size_t i = 0; i < inst.getNumOperands(); i++) {\n    const llvm::MCOperand &op = inst.getOperand(i);\n    if (op.isReg()) {\n      flags |= cache.get(op.getReg());\n    }\n  }\n\n  for (const unsigned implicitRegs : desc.implicit_uses()) {\n    flags |= cache.get(implicitRegs);\n  }\n  for (const unsigned implicitRegs : desc.implicit_defs()) {\n    flags |= cache.get(implicitRegs);\n  }\n\n  // detect implicit FPU instruction\n  if ((desc.TSFlags & llvm::X86II::FPTypeMask) != 0) {\n    if ((desc.TSFlags & llvm::X86II::FPTypeMask) != llvm::X86II::SpecialFP or\n        ((not desc.isReturn()) and (not desc.isCall()))) {\n      flags |= ExecBlockFlags::needFPU;\n    }\n  }\n\n  if ((flags & ExecBlockFlags::needAVX) != 0) {\n    flags |= ExecBlockFlags::needFPU;\n  }\n\n  // enable needFSGS for SYSCALL\n  if (inst.getOpcode() == llvm::X86::SYSCALL) {\n    flags |= ExecBlockFlags::needFSGS;\n  }\n\n  return flags;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/ExecBlockFlags_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef ExecBlockFlags_X86_64_H\n#define ExecBlockFlags_X86_64_H\n\n#include <stdint.h>\n\nnamespace QBDI {\n\ntypedef enum : uint8_t {\n  needAVX = 1 << 0,\n  needFPU = 1 << 1,\n  needFSGS = 1 << 2,\n} ExecBlockFlags;\n\n}\n\n#endif\n"
  },
  {
    "path": "src/Patch/X86_64/ExecBlockPatch_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <stddef.h>\n#include <vector>\n\n#include \"X86InstrInfo.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/State.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/Context.h\"\n#include \"Patch/ExecBlockPatch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Patch/X86_64/ExecBlockFlags_X86_64.h\"\n#include \"Patch/X86_64/Layer2_X86_64.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\nnamespace QBDI {\n\nRelocatableInst::UniquePtrVec getExecBlockPrologue(const LLVMCPU &llvmcpu) {\n  RelocatableInst::UniquePtrVec prologue;\n\n  // Save host SP\n  append(prologue, SaveReg(Reg(REG_SP), Offset(offsetof(Context, hostState.sp)))\n                       .genReloc(llvmcpu));\n  // Restore FPR\n  if (not llvmcpu.hasOptions(Options::OPT_DISABLE_FPR)) {\n    if (not llvmcpu.hasOptions(Options::OPT_DISABLE_OPTIONAL_FPR)) {\n      append(prologue,\n             LoadReg(Reg(0), Offset(offsetof(Context, hostState.executeFlags)))\n                 .genReloc(llvmcpu));\n      prologue.push_back(Test(Reg(0), ExecBlockFlags::needFPU));\n      prologue.push_back(Je(7 + 4));\n    }\n    prologue.push_back(Fxrstor(Offset(offsetof(Context, fprState))));\n    // target je needFPU\n    if (isHostCPUFeaturePresent(\"avx\")) {\n      QBDI_DEBUG(\"AVX support enabled in guest context switches\");\n      // don't restore if not needed\n      if (not llvmcpu.hasOptions(Options::OPT_DISABLE_OPTIONAL_FPR)) {\n        prologue.push_back(Test(Reg(0), ExecBlockFlags::needAVX));\n        if constexpr (is_x86_64)\n          prologue.push_back(Je(16 * 10 + 4));\n        else\n          prologue.push_back(Je(8 * 10 + 4));\n      }\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM0,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm0)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM1,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm1)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM2,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm2)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM3,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm3)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM4,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm4)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM5,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm5)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM6,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm6)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM7,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm7)), 1));\n#if defined(QBDI_ARCH_X86_64)\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM8,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm8)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM9,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm9)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM10,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm10)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM11,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm11)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM12,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm12)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM13,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm13)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM14,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm14)), 1));\n      prologue.push_back(Vinsertf128(\n          llvm::X86::YMM15,\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm15)), 1));\n#endif // QBDI_ARCH_X86_64\n       // target je needAVX\n    }\n  }\n#if defined(QBDI_ARCH_X86_64)\n  // if enable FS GS\n  if (llvmcpu.hasOptions(Options::OPT_ENABLE_FS_GS)) {\n    QBDI_REQUIRE_ABORT(isHostCPUFeaturePresent(\"fsgsbase\"),\n                       \"Need CPU feature fsgsbase\");\n\n    append(prologue,\n           LoadReg(Reg(0), Offset(offsetof(Context, hostState.executeFlags)))\n               .genReloc(llvmcpu));\n    prologue.push_back(Test(Reg(0), ExecBlockFlags::needFSGS));\n    prologue.push_back(Je(5 * 4 + 7 * 4 + 4));\n\n    append(prologue, LoadReg(Reg(3), Offset(offsetof(Context, gprState.fs)))\n                         .genReloc(llvmcpu));\n    append(prologue, LoadReg(Reg(4), Offset(offsetof(Context, gprState.gs)))\n                         .genReloc(llvmcpu));\n    prologue.push_back(Rdfsbase(Reg(1)));\n    prologue.push_back(Rdgsbase(Reg(2)));\n    prologue.push_back(Wrfsbase(Reg(3)));\n    prologue.push_back(Wrgsbase(Reg(4)));\n    append(prologue, SaveReg(Reg(1), Offset(offsetof(Context, hostState.fs)))\n                         .genReloc(llvmcpu));\n    append(prologue, SaveReg(Reg(2), Offset(offsetof(Context, hostState.gs)))\n                         .genReloc(llvmcpu));\n  }\n#endif // QBDI_ARCH_X86_64\n  // Restore EFLAGS\n  append(prologue, LoadReg(Reg(0), Offset(offsetof(Context, gprState.eflags)))\n                       .genReloc(llvmcpu));\n  prologue.push_back(Pushr(Reg(0)));\n  prologue.push_back(Popf());\n  // Restore GPR\n  for (unsigned int i = 0; i < NUM_GPR - 1; i++)\n    append(prologue, LoadReg(Reg(i), Offset(Reg(i))).genReloc(llvmcpu));\n  // Jump selector\n  prologue.push_back(JmpM(Offset(offsetof(Context, hostState.selector))));\n\n  return prologue;\n}\n\nRelocatableInst::UniquePtrVec getExecBlockEpilogue(const LLVMCPU &llvmcpu) {\n  RelocatableInst::UniquePtrVec epilogue;\n\n  // Save GPR\n  for (unsigned int i = 0; i < NUM_GPR - 1; i++)\n    append(epilogue, SaveReg(Reg(i), Offset(Reg(i))).genReloc(llvmcpu));\n  // Restore host SP\n  append(epilogue, LoadReg(Reg(REG_SP), Offset(offsetof(Context, hostState.sp)))\n                       .genReloc(llvmcpu));\n  // Save EFLAGS\n  epilogue.push_back(Pushf());\n  epilogue.push_back(Popr(Reg(0)));\n  append(epilogue, SaveReg(Reg(0), Offset(offsetof(Context, gprState.eflags)))\n                       .genReloc(llvmcpu));\n#if defined(QBDI_ARCH_X86_64)\n  // if enable FS GS\n  if (llvmcpu.hasOptions(Options::OPT_ENABLE_FS_GS)) {\n    QBDI_REQUIRE_ABORT(isHostCPUFeaturePresent(\"fsgsbase\"),\n                       \"Need CPU feature fsgsbase\");\n\n    append(epilogue,\n           LoadReg(Reg(0), Offset(offsetof(Context, hostState.executeFlags)))\n               .genReloc(llvmcpu));\n    epilogue.push_back(Test(Reg(0), ExecBlockFlags::needFSGS));\n    epilogue.push_back(Je(5 * 4 + 7 * 4 + 4));\n\n    append(epilogue, LoadReg(Reg(3), Offset(offsetof(Context, hostState.fs)))\n                         .genReloc(llvmcpu));\n    append(epilogue, LoadReg(Reg(4), Offset(offsetof(Context, hostState.gs)))\n                         .genReloc(llvmcpu));\n    epilogue.push_back(Rdfsbase(Reg(1)));\n    epilogue.push_back(Rdgsbase(Reg(2)));\n    epilogue.push_back(Wrfsbase(Reg(3)));\n    epilogue.push_back(Wrgsbase(Reg(4)));\n    append(epilogue, SaveReg(Reg(1), Offset(offsetof(Context, gprState.fs)))\n                         .genReloc(llvmcpu));\n    append(epilogue, SaveReg(Reg(2), Offset(offsetof(Context, gprState.gs)))\n                         .genReloc(llvmcpu));\n  }\n#endif // QBDI_ARCH_X86_64\n  // Save FPR\n  if (not llvmcpu.hasOptions(Options::OPT_DISABLE_FPR)) {\n    if (not llvmcpu.hasOptions(Options::OPT_DISABLE_OPTIONAL_FPR)) {\n      append(epilogue,\n             LoadReg(Reg(0), Offset(offsetof(Context, hostState.executeFlags)))\n                 .genReloc(llvmcpu));\n      epilogue.push_back(Test(Reg(0), ExecBlockFlags::needFPU));\n      epilogue.push_back(Je(7 + 4));\n    }\n    epilogue.push_back(Fxsave(Offset(offsetof(Context, fprState))));\n    // target je needFPU\n    if (isHostCPUFeaturePresent(\"avx\")) {\n      QBDI_DEBUG(\"AVX support enabled in guest context switches\");\n      // don't save if not needed\n      if (not llvmcpu.hasOptions(Options::OPT_DISABLE_OPTIONAL_FPR)) {\n        epilogue.push_back(Test(Reg(0), ExecBlockFlags::needAVX));\n        if constexpr (is_x86_64)\n          epilogue.push_back(Je(16 * 10 + 4));\n        else\n          epilogue.push_back(Je(8 * 10 + 4));\n      }\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm0)),\n          llvm::X86::YMM0, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm1)),\n          llvm::X86::YMM1, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm2)),\n          llvm::X86::YMM2, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm3)),\n          llvm::X86::YMM3, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm4)),\n          llvm::X86::YMM4, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm5)),\n          llvm::X86::YMM5, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm6)),\n          llvm::X86::YMM6, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm7)),\n          llvm::X86::YMM7, 1));\n#if defined(QBDI_ARCH_X86_64)\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm8)),\n          llvm::X86::YMM8, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm9)),\n          llvm::X86::YMM9, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm10)),\n          llvm::X86::YMM10, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm11)),\n          llvm::X86::YMM11, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm12)),\n          llvm::X86::YMM12, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm13)),\n          llvm::X86::YMM13, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm14)),\n          llvm::X86::YMM14, 1));\n      epilogue.push_back(Vextractf128(\n          Offset(offsetof(Context, fprState) + offsetof(FPRState, ymm15)),\n          llvm::X86::YMM15, 1));\n#endif // QBDI_ARCH_X86_64\n       // target je needAVX\n    }\n  }\n  // return to host\n  epilogue.push_back(Ret());\n\n  return epilogue;\n}\n\n// Patch allowing to terminate a basic block early by writing address into\n// DataBlock[Offset(RIP)]\nRelocatableInst::UniquePtrVec getTerminator(const LLVMCPU &llvmcpu,\n                                            rword address) {\n  RelocatableInst::UniquePtrVec terminator;\n\n  append(terminator, SaveReg(Reg(0), Offset(Reg(0))).genReloc(llvmcpu));\n  terminator.push_back(LoadImm::unique(Reg(0), address));\n  append(terminator, SaveReg(Reg(0), Offset(Reg(REG_PC))).genReloc(llvmcpu));\n  append(terminator, LoadReg(Reg(0), Offset(Reg(0))).genReloc(llvmcpu));\n\n  return terminator;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/InstInfo_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"MCTargetDesc/X86BaseInfo.h\"\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"devVariable.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/X86_64/InstInfo_X86_64.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n/* TODO instruction (no yet supported)\n * See test/Patch/MemoryAccessTable_X86_64.cpp\n */\nnamespace {\n\nconstexpr unsigned READ_8[] = {\n    // clang-format off\n    llvm::X86::ADC8mi,\n    llvm::X86::ADC8mi_EVEX,\n    llvm::X86::ADC8mi_ND,\n    llvm::X86::ADC8mi8,\n    llvm::X86::ADC8mr,\n    llvm::X86::ADC8mr_EVEX,\n    llvm::X86::ADC8mr_ND,\n    llvm::X86::ADC8rm,\n    llvm::X86::ADC8rm_EVEX,\n    llvm::X86::ADC8rm_ND,\n    llvm::X86::ADD8mi,\n    llvm::X86::ADD8mi8,\n    llvm::X86::ADD8mi_EVEX,\n    llvm::X86::ADD8mi_ND,\n    llvm::X86::ADD8mi_NF,\n    llvm::X86::ADD8mi_NF_ND,\n    llvm::X86::ADD8mr,\n    llvm::X86::ADD8mr_EVEX,\n    llvm::X86::ADD8mr_ND,\n    llvm::X86::ADD8mr_NF,\n    llvm::X86::ADD8mr_NF_ND,\n    llvm::X86::ADD8rm,\n    llvm::X86::ADD8rm_EVEX,\n    llvm::X86::ADD8rm_ND,\n    llvm::X86::ADD8rm_NF,\n    llvm::X86::ADD8rm_NF_ND,\n    llvm::X86::AND8mi,\n    llvm::X86::AND8mi8,\n    llvm::X86::AND8mi_EVEX,\n    llvm::X86::AND8mi_ND,\n    llvm::X86::AND8mi_NF,\n    llvm::X86::AND8mi_NF_ND,\n    llvm::X86::AND8mr,\n    llvm::X86::AND8mr_EVEX,\n    llvm::X86::AND8mr_ND,\n    llvm::X86::AND8mr_NF,\n    llvm::X86::AND8mr_NF_ND,\n    llvm::X86::AND8rm,\n    llvm::X86::AND8rm_EVEX,\n    llvm::X86::AND8rm_ND,\n    llvm::X86::AND8rm_NF,\n    llvm::X86::AND8rm_NF_ND,\n    llvm::X86::CCMP8mi,\n    llvm::X86::CCMP8mr,\n    llvm::X86::CCMP8rm,\n    llvm::X86::CMP8mi,\n    llvm::X86::CMP8mi8,\n    llvm::X86::CMP8mr,\n    llvm::X86::CMP8rm,\n    llvm::X86::CMPSB,\n    llvm::X86::CMPXCHG8rm,\n    llvm::X86::CRC32r32m8,\n    llvm::X86::CRC32r32m8_EVEX,\n    llvm::X86::CRC32r64m8,\n    llvm::X86::CRC32r64m8_EVEX,\n    llvm::X86::CTEST8mi,\n    llvm::X86::CTEST8mr,\n    llvm::X86::DEC8m,\n    llvm::X86::DEC8m_EVEX,\n    llvm::X86::DEC8m_ND,\n    llvm::X86::DEC8m_NF,\n    llvm::X86::DEC8m_NF_ND,\n    llvm::X86::DIV8m,\n    llvm::X86::DIV8m_EVEX,\n    llvm::X86::DIV8m_NF,\n    llvm::X86::IDIV8m,\n    llvm::X86::IDIV8m_EVEX,\n    llvm::X86::IDIV8m_NF,\n    llvm::X86::IMUL8m,\n    llvm::X86::IMUL8m_EVEX,\n    llvm::X86::IMUL8m_NF,\n    llvm::X86::INC8m,\n    llvm::X86::INC8m_EVEX,\n    llvm::X86::INC8m_ND,\n    llvm::X86::INC8m_NF,\n    llvm::X86::INC8m_NF_ND,\n    llvm::X86::KMOVBkm,\n    llvm::X86::KMOVBkm_EVEX,\n    llvm::X86::LCMPXCHG8,\n    llvm::X86::LOCK_ADD8mi,\n    llvm::X86::LOCK_ADD8mr,\n    llvm::X86::LOCK_AND8mi,\n    llvm::X86::LOCK_AND8mr,\n    llvm::X86::LOCK_DEC8m,\n    llvm::X86::LOCK_INC8m,\n    llvm::X86::LOCK_OR8mi,\n    llvm::X86::LOCK_OR8mr,\n    llvm::X86::LOCK_SUB8mi,\n    llvm::X86::LOCK_SUB8mr,\n    llvm::X86::LOCK_XOR8mi,\n    llvm::X86::LOCK_XOR8mr,\n    llvm::X86::LODSB,\n    llvm::X86::LXADD8,\n    llvm::X86::MOV8ao16,\n    llvm::X86::MOV8ao32,\n    llvm::X86::MOV8ao64,\n    llvm::X86::MOV8rm,\n    llvm::X86::MOV8rm_NOREX,\n    llvm::X86::MOVSB,\n    llvm::X86::MOVSX16rm8,\n    llvm::X86::MOVSX32rm8,\n    llvm::X86::MOVSX32rm8_NOREX,\n    llvm::X86::MOVSX64rm8,\n    llvm::X86::MOVZX16rm8,\n    llvm::X86::MOVZX32rm8,\n    llvm::X86::MOVZX32rm8_NOREX,\n    llvm::X86::MOVZX64rm8,\n    llvm::X86::MUL8m,\n    llvm::X86::MUL8m_EVEX,\n    llvm::X86::MUL8m_NF,\n    llvm::X86::NEG8m,\n    llvm::X86::NEG8m_EVEX,\n    llvm::X86::NEG8m_ND,\n    llvm::X86::NEG8m_NF,\n    llvm::X86::NEG8m_NF_ND,\n    llvm::X86::NOT8m,\n    llvm::X86::NOT8m_EVEX,\n    llvm::X86::NOT8m_ND,\n    llvm::X86::OR8mi,\n    llvm::X86::OR8mi8,\n    llvm::X86::OR8mi_EVEX,\n    llvm::X86::OR8mi_ND,\n    llvm::X86::OR8mi_NF,\n    llvm::X86::OR8mi_NF_ND,\n    llvm::X86::OR8mr,\n    llvm::X86::OR8mr_EVEX,\n    llvm::X86::OR8mr_ND,\n    llvm::X86::OR8mr_NF,\n    llvm::X86::OR8mr_NF_ND,\n    llvm::X86::OR8rm,\n    llvm::X86::OR8rm_EVEX,\n    llvm::X86::OR8rm_ND,\n    llvm::X86::OR8rm_NF,\n    llvm::X86::OR8rm_NF_ND,\n    llvm::X86::PINSRBrm,\n    llvm::X86::RCL8m1,\n    llvm::X86::RCL8m1_EVEX,\n    llvm::X86::RCL8m1_ND,\n    llvm::X86::RCL8mCL,\n    llvm::X86::RCL8mCL_EVEX,\n    llvm::X86::RCL8mCL_ND,\n    llvm::X86::RCL8mi,\n    llvm::X86::RCL8mi_EVEX,\n    llvm::X86::RCL8mi_ND,\n    llvm::X86::RCR8m1,\n    llvm::X86::RCR8m1_EVEX,\n    llvm::X86::RCR8m1_ND,\n    llvm::X86::RCR8mCL,\n    llvm::X86::RCR8mCL_EVEX,\n    llvm::X86::RCR8mCL_ND,\n    llvm::X86::RCR8mi,\n    llvm::X86::RCR8mi_EVEX,\n    llvm::X86::RCR8mi_ND,\n    llvm::X86::ROL8m1,\n    llvm::X86::ROL8m1_EVEX,\n    llvm::X86::ROL8m1_ND,\n    llvm::X86::ROL8m1_NF,\n    llvm::X86::ROL8m1_NF_ND,\n    llvm::X86::ROL8mCL,\n    llvm::X86::ROL8mCL_EVEX,\n    llvm::X86::ROL8mCL_ND,\n    llvm::X86::ROL8mCL_NF,\n    llvm::X86::ROL8mCL_NF_ND,\n    llvm::X86::ROL8mi,\n    llvm::X86::ROL8mi_EVEX,\n    llvm::X86::ROL8mi_ND,\n    llvm::X86::ROL8mi_NF,\n    llvm::X86::ROL8mi_NF_ND,\n    llvm::X86::ROR8m1,\n    llvm::X86::ROR8m1_EVEX,\n    llvm::X86::ROR8m1_ND,\n    llvm::X86::ROR8m1_NF,\n    llvm::X86::ROR8m1_NF_ND,\n    llvm::X86::ROR8mCL,\n    llvm::X86::ROR8mCL_EVEX,\n    llvm::X86::ROR8mCL_ND,\n    llvm::X86::ROR8mCL_NF,\n    llvm::X86::ROR8mCL_NF_ND,\n    llvm::X86::ROR8mi,\n    llvm::X86::ROR8mi_EVEX,\n    llvm::X86::ROR8mi_ND,\n    llvm::X86::ROR8mi_NF,\n    llvm::X86::ROR8mi_NF_ND,\n    llvm::X86::SAR8m1,\n    llvm::X86::SAR8m1_EVEX,\n    llvm::X86::SAR8m1_ND,\n    llvm::X86::SAR8m1_NF,\n    llvm::X86::SAR8m1_NF_ND,\n    llvm::X86::SAR8mCL,\n    llvm::X86::SAR8mCL_EVEX,\n    llvm::X86::SAR8mCL_ND,\n    llvm::X86::SAR8mCL_NF,\n    llvm::X86::SAR8mCL_NF_ND,\n    llvm::X86::SAR8mi,\n    llvm::X86::SAR8mi_EVEX,\n    llvm::X86::SAR8mi_ND,\n    llvm::X86::SAR8mi_NF,\n    llvm::X86::SAR8mi_NF_ND,\n    llvm::X86::SBB8mi,\n    llvm::X86::SBB8mi_EVEX,\n    llvm::X86::SBB8mi_ND,\n    llvm::X86::SBB8mr,\n    llvm::X86::SBB8mr_EVEX,\n    llvm::X86::SBB8mr_ND,\n    llvm::X86::SBB8rm,\n    llvm::X86::SBB8rm_EVEX,\n    llvm::X86::SBB8rm_ND,\n    llvm::X86::SCASB,\n    llvm::X86::SHL8m1,\n    llvm::X86::SHL8m1_EVEX,\n    llvm::X86::SHL8m1_ND,\n    llvm::X86::SHL8m1_NF,\n    llvm::X86::SHL8m1_NF_ND,\n    llvm::X86::SHL8mCL,\n    llvm::X86::SHL8mCL_EVEX,\n    llvm::X86::SHL8mCL_ND,\n    llvm::X86::SHL8mCL_NF,\n    llvm::X86::SHL8mCL_NF_ND,\n    llvm::X86::SHL8mi,\n    llvm::X86::SHL8mi_EVEX,\n    llvm::X86::SHL8mi_ND,\n    llvm::X86::SHL8mi_NF,\n    llvm::X86::SHL8mi_NF_ND,\n    llvm::X86::SHR8m1,\n    llvm::X86::SHR8m1_EVEX,\n    llvm::X86::SHR8m1_ND,\n    llvm::X86::SHR8m1_NF,\n    llvm::X86::SHR8m1_NF_ND,\n    llvm::X86::SHR8mCL,\n    llvm::X86::SHR8mCL_EVEX,\n    llvm::X86::SHR8mCL_ND,\n    llvm::X86::SHR8mCL_NF,\n    llvm::X86::SHR8mCL_NF_ND,\n    llvm::X86::SHR8mi,\n    llvm::X86::SHR8mi_EVEX,\n    llvm::X86::SHR8mi_ND,\n    llvm::X86::SHR8mi_NF,\n    llvm::X86::SHR8mi_NF_ND,\n    llvm::X86::SUB8mi,\n    llvm::X86::SUB8mi_EVEX,\n    llvm::X86::SUB8mi_ND,\n    llvm::X86::SUB8mi_NF,\n    llvm::X86::SUB8mi_NF_ND,\n    llvm::X86::SUB8mi8,\n    llvm::X86::SUB8mr,\n    llvm::X86::SUB8mr_EVEX,\n    llvm::X86::SUB8mr_ND,\n    llvm::X86::SUB8mr_NF,\n    llvm::X86::SUB8mr_NF_ND,\n    llvm::X86::SUB8rm,\n    llvm::X86::SUB8rm_EVEX,\n    llvm::X86::SUB8rm_ND,\n    llvm::X86::SUB8rm_NF,\n    llvm::X86::SUB8rm_NF_ND,\n    llvm::X86::TEST8mi,\n    llvm::X86::TEST8mr,\n    llvm::X86::VPBROADCASTBYrm,\n    llvm::X86::VPBROADCASTBrm,\n    llvm::X86::VPINSRBrm,\n    llvm::X86::XADD8rm,\n    llvm::X86::XCHG8rm,\n    llvm::X86::XLAT,\n    llvm::X86::XOR8mi,\n    llvm::X86::XOR8mi_EVEX,\n    llvm::X86::XOR8mi_ND,\n    llvm::X86::XOR8mi_NF,\n    llvm::X86::XOR8mi_NF_ND,\n    llvm::X86::XOR8mi8,\n    llvm::X86::XOR8mr,\n    llvm::X86::XOR8mr_EVEX,\n    llvm::X86::XOR8mr_ND,\n    llvm::X86::XOR8mr_NF,\n    llvm::X86::XOR8mr_NF_ND,\n    llvm::X86::XOR8rm,\n    llvm::X86::XOR8rm_EVEX,\n    llvm::X86::XOR8rm_ND,\n    llvm::X86::XOR8rm_NF,\n    llvm::X86::XOR8rm_NF_ND,\n    // clang-format on\n};\n\nconstexpr size_t READ_8_SIZE = sizeof(READ_8) / sizeof(unsigned);\n\nconstexpr unsigned READ_16[] = {\n    // clang-format off\n    llvm::X86::ADC16mi,\n    llvm::X86::ADC16mi_EVEX,\n    llvm::X86::ADC16mi_ND,\n    llvm::X86::ADC16mi8,\n    llvm::X86::ADC16mi8_EVEX,\n    llvm::X86::ADC16mi8_ND,\n    llvm::X86::ADC16mr,\n    llvm::X86::ADC16mr_EVEX,\n    llvm::X86::ADC16mr_ND,\n    llvm::X86::ADC16rm,\n    llvm::X86::ADC16rm_EVEX,\n    llvm::X86::ADC16rm_ND,\n    llvm::X86::ADD16mi,\n    llvm::X86::ADD16mi8,\n    llvm::X86::ADD16mi8_EVEX,\n    llvm::X86::ADD16mi8_ND,\n    llvm::X86::ADD16mi8_NF,\n    llvm::X86::ADD16mi8_NF_ND,\n    llvm::X86::ADD16mi_EVEX,\n    llvm::X86::ADD16mi_ND,\n    llvm::X86::ADD16mi_NF,\n    llvm::X86::ADD16mi_NF_ND,\n    llvm::X86::ADD16mr,\n    llvm::X86::ADD16mr_EVEX,\n    llvm::X86::ADD16mr_ND,\n    llvm::X86::ADD16mr_NF,\n    llvm::X86::ADD16mr_NF_ND,\n    llvm::X86::ADD16rm,\n    llvm::X86::ADD16rm_EVEX,\n    llvm::X86::ADD16rm_ND,\n    llvm::X86::ADD16rm_NF,\n    llvm::X86::ADD16rm_NF_ND,\n    llvm::X86::ADD_FI16m,\n    llvm::X86::AND16mi,\n    llvm::X86::AND16mi8,\n    llvm::X86::AND16mi8_EVEX,\n    llvm::X86::AND16mi8_ND,\n    llvm::X86::AND16mi8_NF,\n    llvm::X86::AND16mi8_NF_ND,\n    llvm::X86::AND16mi_EVEX,\n    llvm::X86::AND16mi_ND,\n    llvm::X86::AND16mi_NF,\n    llvm::X86::AND16mi_NF_ND,\n    llvm::X86::AND16mr,\n    llvm::X86::AND16mr_EVEX,\n    llvm::X86::AND16mr_ND,\n    llvm::X86::AND16mr_NF,\n    llvm::X86::AND16mr_NF_ND,\n    llvm::X86::AND16rm,\n    llvm::X86::AND16rm_EVEX,\n    llvm::X86::AND16rm_ND,\n    llvm::X86::AND16rm_NF,\n    llvm::X86::AND16rm_NF_ND,\n    llvm::X86::ARPL16mr,\n    llvm::X86::BSF16rm,\n    llvm::X86::BSR16rm,\n    llvm::X86::BT16mi8,\n    llvm::X86::BT16mr,\n    llvm::X86::BTC16mi8,\n    llvm::X86::BTC16mr,\n    llvm::X86::BTR16mi8,\n    llvm::X86::BTR16mr,\n    llvm::X86::BTS16mi8,\n    llvm::X86::BTS16mr,\n    llvm::X86::CALL16m,\n    llvm::X86::CALL16m_NT,\n    llvm::X86::CCMP16mi,\n    llvm::X86::CCMP16mi8,\n    llvm::X86::CCMP16mr,\n    llvm::X86::CCMP16rm,\n    llvm::X86::CFCMOV16rm,\n    llvm::X86::CFCMOV16rm_ND,\n    llvm::X86::CMOV16rm,\n    llvm::X86::CMOV16rm_ND,\n    llvm::X86::CMP16mi,\n    llvm::X86::CMP16mi8,\n    llvm::X86::CMP16mr,\n    llvm::X86::CMP16rm,\n    llvm::X86::CMPSW,\n    llvm::X86::CMPXCHG16rm,\n    llvm::X86::CRC32r32m16,\n    llvm::X86::CRC32r32m16_EVEX,\n    llvm::X86::CTEST16mi,\n    llvm::X86::CTEST16mr,\n    llvm::X86::DEC16m,\n    llvm::X86::DEC16m_EVEX,\n    llvm::X86::DEC16m_ND,\n    llvm::X86::DEC16m_NF,\n    llvm::X86::DEC16m_NF_ND,\n    llvm::X86::DIV16m,\n    llvm::X86::DIV16m_EVEX,\n    llvm::X86::DIV16m_NF,\n    llvm::X86::DIVR_FI16m,\n    llvm::X86::DIV_FI16m,\n    llvm::X86::FICOM16m,\n    llvm::X86::FICOMP16m,\n    llvm::X86::FLDCW16m,\n    llvm::X86::IDIV16m,\n    llvm::X86::IDIV16m_EVEX,\n    llvm::X86::IDIV16m_NF,\n    llvm::X86::ILD_F16m,\n    llvm::X86::IMUL16m,\n    llvm::X86::IMUL16m_EVEX,\n    llvm::X86::IMUL16m_NF,\n    llvm::X86::IMUL16rm,\n    llvm::X86::IMUL16rm_EVEX,\n    llvm::X86::IMUL16rm_ND,\n    llvm::X86::IMUL16rm_NF,\n    llvm::X86::IMUL16rm_NF_ND,\n    llvm::X86::IMUL16rmi,\n    llvm::X86::IMUL16rmi8,\n    llvm::X86::IMUL16rmi8_EVEX,\n    llvm::X86::IMUL16rmi8_NF,\n    llvm::X86::IMUL16rmi_EVEX,\n    llvm::X86::IMUL16rmi_NF,\n    llvm::X86::IMULZU16rmi,\n    llvm::X86::IMULZU16rmi8,\n    llvm::X86::INC16m,\n    llvm::X86::INC16m_EVEX,\n    llvm::X86::INC16m_ND,\n    llvm::X86::INC16m_NF,\n    llvm::X86::INC16m_NF_ND,\n    llvm::X86::JMP16m,\n    llvm::X86::JMP16m_NT,\n    llvm::X86::KMOVWkm,\n    llvm::X86::KMOVWkm_EVEX,\n    llvm::X86::LAR16rm,\n    llvm::X86::LAR32rm,\n    llvm::X86::LAR64rm,\n    llvm::X86::LCMPXCHG16,\n    llvm::X86::LKGS16m,\n    llvm::X86::LLDT16m,\n    llvm::X86::LMSW16m,\n    llvm::X86::LOCK_ADD16mi,\n    llvm::X86::LOCK_ADD16mi8,\n    llvm::X86::LOCK_ADD16mr,\n    llvm::X86::LOCK_AND16mi,\n    llvm::X86::LOCK_AND16mi8,\n    llvm::X86::LOCK_AND16mr,\n    llvm::X86::LOCK_BTC16m,\n    llvm::X86::LOCK_BTR16m,\n    llvm::X86::LOCK_BTS16m,\n    llvm::X86::LOCK_DEC16m,\n    llvm::X86::LOCK_INC16m,\n    llvm::X86::LOCK_OR16mi,\n    llvm::X86::LOCK_OR16mi8,\n    llvm::X86::LOCK_OR16mr,\n    llvm::X86::LOCK_SUB16mi,\n    llvm::X86::LOCK_SUB16mi8,\n    llvm::X86::LOCK_SUB16mr,\n    llvm::X86::LOCK_XOR16mi,\n    llvm::X86::LOCK_XOR16mi8,\n    llvm::X86::LOCK_XOR16mr,\n    llvm::X86::LODSW,\n    llvm::X86::LSL16rm,\n    llvm::X86::LSL32rm,\n    llvm::X86::LSL64rm,\n    llvm::X86::LTRm,\n    llvm::X86::LXADD16,\n    llvm::X86::LZCNT16rm,\n    llvm::X86::LZCNT16rm_EVEX,\n    llvm::X86::LZCNT16rm_NF,\n    llvm::X86::MMX_PINSRWrm,\n    llvm::X86::MOV16ao16,\n    llvm::X86::MOV16ao32,\n    llvm::X86::MOV16ao64,\n    llvm::X86::MOV16rm,\n    llvm::X86::MOV16sm,\n    llvm::X86::MOVBE16rm,\n    llvm::X86::MOVBE16rm_EVEX,\n    llvm::X86::MOVSW,\n    llvm::X86::MOVSX32rm16,\n    llvm::X86::MOVSX64rm16,\n    llvm::X86::MOVZX32rm16,\n    llvm::X86::MOVZX64rm16,\n    llvm::X86::MUL16m,\n    llvm::X86::MUL16m_EVEX,\n    llvm::X86::MUL16m_NF,\n    llvm::X86::MUL_FI16m,\n    llvm::X86::NEG16m,\n    llvm::X86::NEG16m_EVEX,\n    llvm::X86::NEG16m_ND,\n    llvm::X86::NEG16m_NF,\n    llvm::X86::NEG16m_NF_ND,\n    llvm::X86::NOT16m,\n    llvm::X86::NOT16m_EVEX,\n    llvm::X86::NOT16m_ND,\n    llvm::X86::OR16mi,\n    llvm::X86::OR16mi8,\n    llvm::X86::OR16mi8_EVEX,\n    llvm::X86::OR16mi8_ND,\n    llvm::X86::OR16mi8_NF,\n    llvm::X86::OR16mi8_NF_ND,\n    llvm::X86::OR16mi_EVEX,\n    llvm::X86::OR16mi_ND,\n    llvm::X86::OR16mi_NF,\n    llvm::X86::OR16mi_NF_ND,\n    llvm::X86::OR16mr,\n    llvm::X86::OR16mr_EVEX,\n    llvm::X86::OR16mr_ND,\n    llvm::X86::OR16mr_NF,\n    llvm::X86::OR16mr_NF_ND,\n    llvm::X86::OR16rm,\n    llvm::X86::OR16rm_EVEX,\n    llvm::X86::OR16rm_ND,\n    llvm::X86::OR16rm_NF,\n    llvm::X86::OR16rm_NF_ND,\n    llvm::X86::PINSRWrm,\n    llvm::X86::PMOVSXBQrm,\n    llvm::X86::PMOVZXBQrm,\n    llvm::X86::POPCNT16rm,\n    llvm::X86::POPCNT16rm_EVEX,\n    llvm::X86::POPCNT16rm_NF,\n    llvm::X86::PUSH16rmm,\n    llvm::X86::RCL16m1,\n    llvm::X86::RCL16m1_EVEX,\n    llvm::X86::RCL16m1_ND,\n    llvm::X86::RCL16mCL,\n    llvm::X86::RCL16mCL_EVEX,\n    llvm::X86::RCL16mCL_ND,\n    llvm::X86::RCL16mi,\n    llvm::X86::RCL16mi_EVEX,\n    llvm::X86::RCL16mi_ND,\n    llvm::X86::RCR16m1,\n    llvm::X86::RCR16m1_EVEX,\n    llvm::X86::RCR16m1_ND,\n    llvm::X86::RCR16mCL,\n    llvm::X86::RCR16mCL_EVEX,\n    llvm::X86::RCR16mCL_ND,\n    llvm::X86::RCR16mi,\n    llvm::X86::RCR16mi_EVEX,\n    llvm::X86::RCR16mi_ND,\n    llvm::X86::ROL16m1,\n    llvm::X86::ROL16m1_EVEX,\n    llvm::X86::ROL16m1_ND,\n    llvm::X86::ROL16m1_NF,\n    llvm::X86::ROL16m1_NF_ND,\n    llvm::X86::ROL16mCL,\n    llvm::X86::ROL16mCL_EVEX,\n    llvm::X86::ROL16mCL_ND,\n    llvm::X86::ROL16mCL_NF,\n    llvm::X86::ROL16mCL_NF_ND,\n    llvm::X86::ROL16mi,\n    llvm::X86::ROL16mi_EVEX,\n    llvm::X86::ROL16mi_ND,\n    llvm::X86::ROL16mi_NF,\n    llvm::X86::ROL16mi_NF_ND,\n    llvm::X86::ROR16m1,\n    llvm::X86::ROR16m1_EVEX,\n    llvm::X86::ROR16m1_ND,\n    llvm::X86::ROR16m1_NF,\n    llvm::X86::ROR16m1_NF_ND,\n    llvm::X86::ROR16mCL,\n    llvm::X86::ROR16mCL_EVEX,\n    llvm::X86::ROR16mCL_ND,\n    llvm::X86::ROR16mCL_NF,\n    llvm::X86::ROR16mCL_NF_ND,\n    llvm::X86::ROR16mi,\n    llvm::X86::ROR16mi_EVEX,\n    llvm::X86::ROR16mi_ND,\n    llvm::X86::ROR16mi_NF,\n    llvm::X86::ROR16mi_NF_ND,\n    llvm::X86::SAR16m1,\n    llvm::X86::SAR16m1_EVEX,\n    llvm::X86::SAR16m1_ND,\n    llvm::X86::SAR16m1_NF,\n    llvm::X86::SAR16m1_NF_ND,\n    llvm::X86::SAR16mCL,\n    llvm::X86::SAR16mCL_EVEX,\n    llvm::X86::SAR16mCL_ND,\n    llvm::X86::SAR16mCL_NF,\n    llvm::X86::SAR16mCL_NF_ND,\n    llvm::X86::SAR16mi,\n    llvm::X86::SAR16mi_EVEX,\n    llvm::X86::SAR16mi_ND,\n    llvm::X86::SAR16mi_NF,\n    llvm::X86::SAR16mi_NF_ND,\n    llvm::X86::SBB16mi,\n    llvm::X86::SBB16mi8,\n    llvm::X86::SBB16mi8_EVEX,\n    llvm::X86::SBB16mi8_ND,\n    llvm::X86::SBB16mi_EVEX,\n    llvm::X86::SBB16mi_ND,\n    llvm::X86::SBB16mr,\n    llvm::X86::SBB16mr_EVEX,\n    llvm::X86::SBB16mr_ND,\n    llvm::X86::SBB16rm,\n    llvm::X86::SBB16rm_EVEX,\n    llvm::X86::SBB16rm_ND,\n    llvm::X86::SCASW,\n    llvm::X86::SHL16m1,\n    llvm::X86::SHL16m1_EVEX,\n    llvm::X86::SHL16m1_ND,\n    llvm::X86::SHL16m1_NF,\n    llvm::X86::SHL16m1_NF_ND,\n    llvm::X86::SHL16mCL,\n    llvm::X86::SHL16mCL_EVEX,\n    llvm::X86::SHL16mCL_ND,\n    llvm::X86::SHL16mCL_NF,\n    llvm::X86::SHL16mCL_NF_ND,\n    llvm::X86::SHL16mi,\n    llvm::X86::SHL16mi_EVEX,\n    llvm::X86::SHL16mi_ND,\n    llvm::X86::SHL16mi_NF,\n    llvm::X86::SHL16mi_NF_ND,\n    llvm::X86::SHLD16mrCL,\n    llvm::X86::SHLD16mrCL_EVEX,\n    llvm::X86::SHLD16mrCL_ND,\n    llvm::X86::SHLD16mrCL_NF,\n    llvm::X86::SHLD16mrCL_NF_ND,\n    llvm::X86::SHLD16mri8,\n    llvm::X86::SHLD16mri8_EVEX,\n    llvm::X86::SHLD16mri8_ND,\n    llvm::X86::SHLD16mri8_NF,\n    llvm::X86::SHLD16mri8_NF_ND,\n    llvm::X86::SHR16m1,\n    llvm::X86::SHR16m1_EVEX,\n    llvm::X86::SHR16m1_ND,\n    llvm::X86::SHR16m1_NF,\n    llvm::X86::SHR16m1_NF_ND,\n    llvm::X86::SHR16mCL,\n    llvm::X86::SHR16mCL_EVEX,\n    llvm::X86::SHR16mCL_ND,\n    llvm::X86::SHR16mCL_NF,\n    llvm::X86::SHR16mCL_NF_ND,\n    llvm::X86::SHR16mi,\n    llvm::X86::SHR16mi_EVEX,\n    llvm::X86::SHR16mi_ND,\n    llvm::X86::SHR16mi_NF,\n    llvm::X86::SHR16mi_NF_ND,\n    llvm::X86::SHRD16mrCL,\n    llvm::X86::SHRD16mrCL_EVEX,\n    llvm::X86::SHRD16mrCL_ND,\n    llvm::X86::SHRD16mrCL_NF,\n    llvm::X86::SHRD16mrCL_NF_ND,\n    llvm::X86::SHRD16mri8,\n    llvm::X86::SHRD16mri8_EVEX,\n    llvm::X86::SHRD16mri8_ND,\n    llvm::X86::SHRD16mri8_NF,\n    llvm::X86::SHRD16mri8_NF_ND,\n    llvm::X86::SUB16mi,\n    llvm::X86::SUB16mi8,\n    llvm::X86::SUB16mi8_EVEX,\n    llvm::X86::SUB16mi8_ND,\n    llvm::X86::SUB16mi8_NF,\n    llvm::X86::SUB16mi8_NF_ND,\n    llvm::X86::SUB16mi_EVEX,\n    llvm::X86::SUB16mi_ND,\n    llvm::X86::SUB16mi_NF,\n    llvm::X86::SUB16mi_NF_ND,\n    llvm::X86::SUB16mr,\n    llvm::X86::SUB16mr_EVEX,\n    llvm::X86::SUB16mr_ND,\n    llvm::X86::SUB16mr_NF,\n    llvm::X86::SUB16mr_NF_ND,\n    llvm::X86::SUB16rm,\n    llvm::X86::SUB16rm_EVEX,\n    llvm::X86::SUB16rm_ND,\n    llvm::X86::SUB16rm_NF,\n    llvm::X86::SUB16rm_NF_ND,\n    llvm::X86::SUBR_FI16m,\n    llvm::X86::SUB_FI16m,\n    llvm::X86::TEST16mi,\n    llvm::X86::TEST16mr,\n    llvm::X86::TZCNT16rm,\n    llvm::X86::TZCNT16rm_EVEX,\n    llvm::X86::TZCNT16rm_NF,\n    llvm::X86::VADDSHZrm,\n    llvm::X86::VBCSTNEBF162PSYrm,\n    llvm::X86::VBCSTNEBF162PSrm,\n    llvm::X86::VBCSTNESH2PSYrm,\n    llvm::X86::VBCSTNESH2PSrm,\n    llvm::X86::VCOMISHZrm,\n    llvm::X86::VCOMISHZrm_Int,\n    llvm::X86::VCVTSH2SDZrm,\n    llvm::X86::VCVTSH2SI64Zrm_Int,\n    llvm::X86::VCVTSH2SIZrm_Int,\n    llvm::X86::VCVTSH2SSZrm,\n    llvm::X86::VCVTSH2USI64Zrm_Int,\n    llvm::X86::VCVTSH2USIZrm_Int,\n    llvm::X86::VDIVSHZrm,\n    llvm::X86::VERRm,\n    llvm::X86::VERWm,\n    llvm::X86::VMAXCSHZrm,\n    llvm::X86::VMAXSHZrm,\n    llvm::X86::VMINCSHZrm,\n    llvm::X86::VMINSHZrm,\n    llvm::X86::VMULSHZrm,\n    llvm::X86::VPBROADCASTWYrm,\n    llvm::X86::VPBROADCASTWrm,\n    llvm::X86::VPINSRWrm,\n    llvm::X86::VPMOVSXBQrm,\n    llvm::X86::VPMOVZXBQrm,\n    llvm::X86::VSQRTSHZm,\n    llvm::X86::VSUBSHZrm,\n    llvm::X86::VUCOMISHZrm,\n    llvm::X86::VUCOMISHZrm_Int,\n    llvm::X86::XADD16rm,\n    llvm::X86::XCHG16rm,\n    llvm::X86::XOR16mi,\n    llvm::X86::XOR16mi8,\n    llvm::X86::XOR16mi8_EVEX,\n    llvm::X86::XOR16mi8_ND,\n    llvm::X86::XOR16mi8_NF,\n    llvm::X86::XOR16mi8_NF_ND,\n    llvm::X86::XOR16mi_EVEX,\n    llvm::X86::XOR16mi_ND,\n    llvm::X86::XOR16mi_NF,\n    llvm::X86::XOR16mi_NF_ND,\n    llvm::X86::XOR16mr,\n    llvm::X86::XOR16mr_EVEX,\n    llvm::X86::XOR16mr_ND,\n    llvm::X86::XOR16mr_NF,\n    llvm::X86::XOR16mr_NF_ND,\n    llvm::X86::XOR16rm,\n    llvm::X86::XOR16rm_EVEX,\n    llvm::X86::XOR16rm_ND,\n    llvm::X86::XOR16rm_NF,\n    llvm::X86::XOR16rm_NF_ND,\n    // clang-format on\n};\n\nconstexpr size_t READ_16_SIZE = sizeof(READ_16) / sizeof(unsigned);\n\nconstexpr unsigned READ_32[] = {\n    // clang-format off\n    llvm::X86::AADD32mr,\n    llvm::X86::AADD32mr_EVEX,\n    llvm::X86::AAND32mr,\n    llvm::X86::AAND32mr_EVEX,\n    llvm::X86::ADC32mi,\n    llvm::X86::ADC32mi8,\n    llvm::X86::ADC32mi8_EVEX,\n    llvm::X86::ADC32mi8_ND,\n    llvm::X86::ADC32mi_EVEX,\n    llvm::X86::ADC32mi_ND,\n    llvm::X86::ADC32mr,\n    llvm::X86::ADC32mr_EVEX,\n    llvm::X86::ADC32mr_ND,\n    llvm::X86::ADC32rm,\n    llvm::X86::ADC32rm_EVEX,\n    llvm::X86::ADC32rm_ND,\n    llvm::X86::ADCX32rm,\n    llvm::X86::ADCX32rm_EVEX,\n    llvm::X86::ADCX32rm_ND,\n    llvm::X86::ADD32mi,\n    llvm::X86::ADD32mi8,\n    llvm::X86::ADD32mi8_EVEX,\n    llvm::X86::ADD32mi8_ND,\n    llvm::X86::ADD32mi8_NF,\n    llvm::X86::ADD32mi8_NF_ND,\n    llvm::X86::ADD32mi_EVEX,\n    llvm::X86::ADD32mi_ND,\n    llvm::X86::ADD32mi_NF,\n    llvm::X86::ADD32mi_NF_ND,\n    llvm::X86::ADD32mr,\n    llvm::X86::ADD32mr_EVEX,\n    llvm::X86::ADD32mr_ND,\n    llvm::X86::ADD32mr_NF,\n    llvm::X86::ADD32mr_NF_ND,\n    llvm::X86::ADD32rm,\n    llvm::X86::ADD32rm_EVEX,\n    llvm::X86::ADD32rm_ND,\n    llvm::X86::ADD32rm_NF,\n    llvm::X86::ADD32rm_NF_ND,\n    llvm::X86::ADDSSrm,\n    llvm::X86::ADDSSrm_Int,\n    llvm::X86::ADD_F32m,\n    llvm::X86::ADD_FI32m,\n    llvm::X86::ADOX32rm,\n    llvm::X86::ADOX32rm_EVEX,\n    llvm::X86::ADOX32rm_ND,\n    llvm::X86::AND32mi,\n    llvm::X86::AND32mi8,\n    llvm::X86::AND32mi8_EVEX,\n    llvm::X86::AND32mi8_ND,\n    llvm::X86::AND32mi8_NF,\n    llvm::X86::AND32mi8_NF_ND,\n    llvm::X86::AND32mi_EVEX,\n    llvm::X86::AND32mi_ND,\n    llvm::X86::AND32mi_NF,\n    llvm::X86::AND32mi_NF_ND,\n    llvm::X86::AND32mr,\n    llvm::X86::AND32mr_EVEX,\n    llvm::X86::AND32mr_ND,\n    llvm::X86::AND32mr_NF,\n    llvm::X86::AND32mr_NF_ND,\n    llvm::X86::AND32rm,\n    llvm::X86::AND32rm_EVEX,\n    llvm::X86::AND32rm_ND,\n    llvm::X86::AND32rm_NF,\n    llvm::X86::AND32rm_NF_ND,\n    llvm::X86::ANDN32rm,\n    llvm::X86::ANDN32rm_EVEX,\n    llvm::X86::AOR32mr,\n    llvm::X86::AOR32mr_EVEX,\n    llvm::X86::AXOR32mr,\n    llvm::X86::AXOR32mr_EVEX,\n    llvm::X86::BEXTR32rm,\n    llvm::X86::BEXTR32rm_EVEX,\n    llvm::X86::BEXTR32rm_NF,\n    llvm::X86::BEXTRI32mi,\n    llvm::X86::BLCFILL32rm,\n    llvm::X86::BLCI32rm,\n    llvm::X86::BLCIC32rm,\n    llvm::X86::BLCMSK32rm,\n    llvm::X86::BLCS32rm,\n    llvm::X86::BLSFILL32rm,\n    llvm::X86::BLSI32rm,\n    llvm::X86::BLSI32rm_EVEX,\n    llvm::X86::BLSI32rm_NF,\n    llvm::X86::BLSIC32rm,\n    llvm::X86::BLSMSK32rm,\n    llvm::X86::BLSMSK32rm_EVEX,\n    llvm::X86::BLSMSK32rm_NF,\n    llvm::X86::BLSR32rm,\n    llvm::X86::BLSR32rm_EVEX,\n    llvm::X86::BLSR32rm_NF,\n    llvm::X86::BOUNDS16rm,\n    llvm::X86::BSF32rm,\n    llvm::X86::BSR32rm,\n    llvm::X86::BT32mi8,\n    llvm::X86::BT32mr,\n    llvm::X86::BTC32mi8,\n    llvm::X86::BTC32mr,\n    llvm::X86::BTR32mi8,\n    llvm::X86::BTR32mr,\n    llvm::X86::BTS32mi8,\n    llvm::X86::BTS32mr,\n    llvm::X86::BZHI32rm,\n    llvm::X86::BZHI32rm_EVEX,\n    llvm::X86::BZHI32rm_NF,\n    llvm::X86::CALL32m,\n    llvm::X86::CALL32m_NT,\n    llvm::X86::CCMP32mi,\n    llvm::X86::CCMP32mi8,\n    llvm::X86::CCMP32mr,\n    llvm::X86::CCMP32rm,\n    llvm::X86::CFCMOV32rm,\n    llvm::X86::CFCMOV32rm_ND,\n    llvm::X86::CMOV32rm,\n    llvm::X86::CMOV32rm_ND,\n    llvm::X86::CMP32mi,\n    llvm::X86::CMP32mi8,\n    llvm::X86::CMP32mr,\n    llvm::X86::CMP32rm,\n    llvm::X86::CMPCCXADDmr32,\n    llvm::X86::CMPCCXADDmr32_EVEX,\n    llvm::X86::CMPSL,\n    llvm::X86::CMPSSrmi,\n    llvm::X86::CMPSSrmi_Int,\n    llvm::X86::CMPXCHG32rm,\n    llvm::X86::COMISSrm,\n    llvm::X86::COMISSrm_Int,\n    llvm::X86::CRC32r32m32,\n    llvm::X86::CRC32r32m32_EVEX,\n    llvm::X86::CTEST32mi,\n    llvm::X86::CTEST32mr,\n    llvm::X86::CVTSI2SDrm,\n    llvm::X86::CVTSI2SDrm_Int,\n    llvm::X86::CVTSI2SSrm,\n    llvm::X86::CVTSI2SSrm_Int,\n    llvm::X86::CVTSS2SDrm,\n    llvm::X86::CVTSS2SDrm_Int,\n    llvm::X86::CVTSS2SI64rm,\n    llvm::X86::CVTSS2SI64rm_Int,\n    llvm::X86::CVTSS2SIrm,\n    llvm::X86::CVTSS2SIrm_Int,\n    llvm::X86::CVTTSS2SI64rm,\n    llvm::X86::CVTTSS2SI64rm_Int,\n    llvm::X86::CVTTSS2SIrm,\n    llvm::X86::CVTTSS2SIrm_Int,\n    llvm::X86::DEC32m,\n    llvm::X86::DEC32m_EVEX,\n    llvm::X86::DEC32m_ND,\n    llvm::X86::DEC32m_NF,\n    llvm::X86::DEC32m_NF_ND,\n    llvm::X86::DIV32m,\n    llvm::X86::DIV32m_EVEX,\n    llvm::X86::DIV32m_NF,\n    llvm::X86::DIVR_F32m,\n    llvm::X86::DIVR_FI32m,\n    llvm::X86::DIVSSrm,\n    llvm::X86::DIVSSrm_Int,\n    llvm::X86::DIV_F32m,\n    llvm::X86::DIV_FI32m,\n    llvm::X86::FCOM32m,\n    llvm::X86::FCOMP32m,\n    llvm::X86::FICOM32m,\n    llvm::X86::FICOMP32m,\n    llvm::X86::IDIV32m,\n    llvm::X86::IDIV32m_EVEX,\n    llvm::X86::IDIV32m_NF,\n    llvm::X86::ILD_F32m,\n    llvm::X86::IMUL32m,\n    llvm::X86::IMUL32m_EVEX,\n    llvm::X86::IMUL32m_NF,\n    llvm::X86::IMUL32rm,\n    llvm::X86::IMUL32rm_EVEX,\n    llvm::X86::IMUL32rm_ND,\n    llvm::X86::IMUL32rm_NF,\n    llvm::X86::IMUL32rm_NF_ND,\n    llvm::X86::IMUL32rmi,\n    llvm::X86::IMUL32rmi8,\n    llvm::X86::IMUL32rmi8_EVEX,\n    llvm::X86::IMUL32rmi8_NF,\n    llvm::X86::IMUL32rmi_EVEX,\n    llvm::X86::IMUL32rmi_NF,\n    llvm::X86::IMULZU32rmi,\n    llvm::X86::IMULZU32rmi8,\n    llvm::X86::INC32m,\n    llvm::X86::INC32m_EVEX,\n    llvm::X86::INC32m_ND,\n    llvm::X86::INC32m_NF,\n    llvm::X86::INC32m_NF_ND,\n    llvm::X86::INSERTPSrm,\n    llvm::X86::JMP32m,\n    llvm::X86::JMP32m_NT,\n    llvm::X86::KMOVDkm,\n    llvm::X86::KMOVDkm_EVEX,\n    llvm::X86::LCMPXCHG32,\n    llvm::X86::LDMXCSR,\n    llvm::X86::LD_F32m,\n    llvm::X86::LOCK_ADD32mi,\n    llvm::X86::LOCK_ADD32mi8,\n    llvm::X86::LOCK_ADD32mr,\n    llvm::X86::LOCK_AND32mi,\n    llvm::X86::LOCK_AND32mi8,\n    llvm::X86::LOCK_AND32mr,\n    llvm::X86::LOCK_BTC32m,\n    llvm::X86::LOCK_BTR32m,\n    llvm::X86::LOCK_BTS32m,\n    llvm::X86::LOCK_DEC32m,\n    llvm::X86::LOCK_INC32m,\n    llvm::X86::LOCK_OR32mi,\n    llvm::X86::LOCK_OR32mi8,\n    llvm::X86::LOCK_OR32mr,\n    llvm::X86::LOCK_SUB32mi,\n    llvm::X86::LOCK_SUB32mi8,\n    llvm::X86::LOCK_SUB32mr,\n    llvm::X86::LOCK_XOR32mi,\n    llvm::X86::LOCK_XOR32mi8,\n    llvm::X86::LOCK_XOR32mr,\n    llvm::X86::LODSL,\n    llvm::X86::LXADD32,\n    llvm::X86::LZCNT32rm,\n    llvm::X86::LZCNT32rm_EVEX,\n    llvm::X86::LZCNT32rm_NF,\n    llvm::X86::MAXSSrm,\n    llvm::X86::MAXSSrm_Int,\n    llvm::X86::MINSSrm,\n    llvm::X86::MINSSrm_Int,\n    llvm::X86::MMX_MOVD64rm,\n    llvm::X86::MMX_PUNPCKLBWrm,\n    llvm::X86::MMX_PUNPCKLDQrm,\n    llvm::X86::MMX_PUNPCKLWDrm,\n    llvm::X86::MOV32ao16,\n    llvm::X86::MOV32ao32,\n    llvm::X86::MOV32ao64,\n    llvm::X86::MOV32rm,\n    llvm::X86::MOVBE32rm,\n    llvm::X86::MOVBE32rm_EVEX,\n    llvm::X86::MOVDI2PDIrm,\n    llvm::X86::MOVSL,\n    llvm::X86::MOVSSrm,\n    llvm::X86::MOVSSrm_alt,\n    llvm::X86::MOVSX64rm32,\n    llvm::X86::MUL32m,\n    llvm::X86::MUL32m_EVEX,\n    llvm::X86::MUL32m_NF,\n    llvm::X86::MULSSrm,\n    llvm::X86::MULSSrm_Int,\n    llvm::X86::MULX32rm,\n    llvm::X86::MULX32rm_EVEX,\n    llvm::X86::MUL_F32m,\n    llvm::X86::MUL_FI32m,\n    llvm::X86::NEG32m,\n    llvm::X86::NEG32m_EVEX,\n    llvm::X86::NEG32m_ND,\n    llvm::X86::NEG32m_NF,\n    llvm::X86::NEG32m_NF_ND,\n    llvm::X86::NOT32m,\n    llvm::X86::NOT32m_EVEX,\n    llvm::X86::NOT32m_ND,\n    llvm::X86::OR32mi,\n    llvm::X86::OR32mi8,\n    llvm::X86::OR32mi8Locked,\n    llvm::X86::OR32mi8_EVEX,\n    llvm::X86::OR32mi8_ND,\n    llvm::X86::OR32mi8_NF,\n    llvm::X86::OR32mi8_NF_ND,\n    llvm::X86::OR32mi_EVEX,\n    llvm::X86::OR32mi_ND,\n    llvm::X86::OR32mi_NF,\n    llvm::X86::OR32mi_NF_ND,\n    llvm::X86::OR32mr,\n    llvm::X86::OR32mr_EVEX,\n    llvm::X86::OR32mr_ND,\n    llvm::X86::OR32mr_NF,\n    llvm::X86::OR32mr_NF_ND,\n    llvm::X86::OR32rm,\n    llvm::X86::OR32rm_EVEX,\n    llvm::X86::OR32rm_ND,\n    llvm::X86::OR32rm_NF,\n    llvm::X86::OR32rm_NF_ND,\n    llvm::X86::PDEP32rm,\n    llvm::X86::PDEP32rm_EVEX,\n    llvm::X86::PEXT32rm,\n    llvm::X86::PEXT32rm_EVEX,\n    llvm::X86::PINSRDrm,\n    llvm::X86::PMOVSXBDrm,\n    llvm::X86::PMOVSXWQrm,\n    llvm::X86::PMOVZXBDrm,\n    llvm::X86::PMOVZXWQrm,\n    llvm::X86::POPCNT32rm,\n    llvm::X86::POPCNT32rm_EVEX,\n    llvm::X86::POPCNT32rm_NF,\n    llvm::X86::PTWRITEm,\n    llvm::X86::PUSH32rmm,\n    llvm::X86::RCL32m1,\n    llvm::X86::RCL32m1_EVEX,\n    llvm::X86::RCL32m1_ND,\n    llvm::X86::RCL32mCL,\n    llvm::X86::RCL32mCL_EVEX,\n    llvm::X86::RCL32mCL_ND,\n    llvm::X86::RCL32mi,\n    llvm::X86::RCL32mi_EVEX,\n    llvm::X86::RCL32mi_ND,\n    llvm::X86::RCPSSm,\n    llvm::X86::RCPSSm_Int,\n    llvm::X86::RCR32m1,\n    llvm::X86::RCR32m1_EVEX,\n    llvm::X86::RCR32m1_ND,\n    llvm::X86::RCR32mCL,\n    llvm::X86::RCR32mCL_EVEX,\n    llvm::X86::RCR32mCL_ND,\n    llvm::X86::RCR32mi,\n    llvm::X86::RCR32mi_EVEX,\n    llvm::X86::RCR32mi_ND,\n    llvm::X86::ROL32m1,\n    llvm::X86::ROL32m1_EVEX,\n    llvm::X86::ROL32m1_ND,\n    llvm::X86::ROL32m1_NF,\n    llvm::X86::ROL32m1_NF_ND,\n    llvm::X86::ROL32mCL,\n    llvm::X86::ROL32mCL_EVEX,\n    llvm::X86::ROL32mCL_ND,\n    llvm::X86::ROL32mCL_NF,\n    llvm::X86::ROL32mCL_NF_ND,\n    llvm::X86::ROL32mi,\n    llvm::X86::ROL32mi_EVEX,\n    llvm::X86::ROL32mi_ND,\n    llvm::X86::ROL32mi_NF,\n    llvm::X86::ROL32mi_NF_ND,\n    llvm::X86::ROR32m1,\n    llvm::X86::ROR32m1_EVEX,\n    llvm::X86::ROR32m1_ND,\n    llvm::X86::ROR32m1_NF,\n    llvm::X86::ROR32m1_NF_ND,\n    llvm::X86::ROR32mCL,\n    llvm::X86::ROR32mCL_EVEX,\n    llvm::X86::ROR32mCL_ND,\n    llvm::X86::ROR32mCL_NF,\n    llvm::X86::ROR32mCL_NF_ND,\n    llvm::X86::ROR32mi,\n    llvm::X86::ROR32mi_EVEX,\n    llvm::X86::ROR32mi_ND,\n    llvm::X86::ROR32mi_NF,\n    llvm::X86::ROR32mi_NF_ND,\n    llvm::X86::RORX32mi,\n    llvm::X86::RORX32mi_EVEX,\n    llvm::X86::ROUNDSSmi,\n    llvm::X86::ROUNDSSmi_Int,\n    llvm::X86::RSQRTSSm,\n    llvm::X86::RSQRTSSm_Int,\n    llvm::X86::SAR32m1,\n    llvm::X86::SAR32m1_EVEX,\n    llvm::X86::SAR32m1_ND,\n    llvm::X86::SAR32m1_NF,\n    llvm::X86::SAR32m1_NF_ND,\n    llvm::X86::SAR32mCL,\n    llvm::X86::SAR32mCL_EVEX,\n    llvm::X86::SAR32mCL_ND,\n    llvm::X86::SAR32mCL_NF,\n    llvm::X86::SAR32mCL_NF_ND,\n    llvm::X86::SAR32mi,\n    llvm::X86::SAR32mi_EVEX,\n    llvm::X86::SAR32mi_ND,\n    llvm::X86::SAR32mi_NF,\n    llvm::X86::SAR32mi_NF_ND,\n    llvm::X86::SARX32rm,\n    llvm::X86::SARX32rm_EVEX,\n    llvm::X86::SBB32mi,\n    llvm::X86::SBB32mi8,\n    llvm::X86::SBB32mi8_EVEX,\n    llvm::X86::SBB32mi8_ND,\n    llvm::X86::SBB32mi_EVEX,\n    llvm::X86::SBB32mi_ND,\n    llvm::X86::SBB32mr,\n    llvm::X86::SBB32mr_EVEX,\n    llvm::X86::SBB32mr_ND,\n    llvm::X86::SBB32rm,\n    llvm::X86::SBB32rm_EVEX,\n    llvm::X86::SBB32rm_ND,\n    llvm::X86::SCASL,\n    llvm::X86::SHL32m1,\n    llvm::X86::SHL32m1_EVEX,\n    llvm::X86::SHL32m1_ND,\n    llvm::X86::SHL32m1_NF,\n    llvm::X86::SHL32m1_NF_ND,\n    llvm::X86::SHL32mCL,\n    llvm::X86::SHL32mCL_EVEX,\n    llvm::X86::SHL32mCL_ND,\n    llvm::X86::SHL32mCL_NF,\n    llvm::X86::SHL32mCL_NF_ND,\n    llvm::X86::SHL32mi,\n    llvm::X86::SHL32mi_EVEX,\n    llvm::X86::SHL32mi_ND,\n    llvm::X86::SHL32mi_NF,\n    llvm::X86::SHL32mi_NF_ND,\n    llvm::X86::SHLD32mrCL,\n    llvm::X86::SHLD32mrCL_EVEX,\n    llvm::X86::SHLD32mrCL_ND,\n    llvm::X86::SHLD32mrCL_NF,\n    llvm::X86::SHLD32mrCL_NF_ND,\n    llvm::X86::SHLD32mri8,\n    llvm::X86::SHLD32mri8_EVEX,\n    llvm::X86::SHLD32mri8_ND,\n    llvm::X86::SHLD32mri8_NF,\n    llvm::X86::SHLD32mri8_NF_ND,\n    llvm::X86::SHLX32rm,\n    llvm::X86::SHLX32rm_EVEX,\n    llvm::X86::SHR32m1,\n    llvm::X86::SHR32m1_EVEX,\n    llvm::X86::SHR32m1_ND,\n    llvm::X86::SHR32m1_NF,\n    llvm::X86::SHR32m1_NF_ND,\n    llvm::X86::SHR32mCL,\n    llvm::X86::SHR32mCL_EVEX,\n    llvm::X86::SHR32mCL_ND,\n    llvm::X86::SHR32mCL_NF,\n    llvm::X86::SHR32mCL_NF_ND,\n    llvm::X86::SHR32mi,\n    llvm::X86::SHR32mi_EVEX,\n    llvm::X86::SHR32mi_ND,\n    llvm::X86::SHR32mi_NF,\n    llvm::X86::SHR32mi_NF_ND,\n    llvm::X86::SHRD32mrCL,\n    llvm::X86::SHRD32mrCL_EVEX,\n    llvm::X86::SHRD32mrCL_ND,\n    llvm::X86::SHRD32mrCL_NF,\n    llvm::X86::SHRD32mrCL_NF_ND,\n    llvm::X86::SHRD32mri8,\n    llvm::X86::SHRD32mri8_EVEX,\n    llvm::X86::SHRD32mri8_ND,\n    llvm::X86::SHRD32mri8_NF,\n    llvm::X86::SHRD32mri8_NF_ND,\n    llvm::X86::SHRX32rm,\n    llvm::X86::SHRX32rm_EVEX,\n    llvm::X86::SQRTSSm,\n    llvm::X86::SQRTSSm_Int,\n    llvm::X86::SUB32mi,\n    llvm::X86::SUB32mi8,\n    llvm::X86::SUB32mi8_EVEX,\n    llvm::X86::SUB32mi8_ND,\n    llvm::X86::SUB32mi8_NF,\n    llvm::X86::SUB32mi8_NF_ND,\n    llvm::X86::SUB32mi_EVEX,\n    llvm::X86::SUB32mi_ND,\n    llvm::X86::SUB32mi_NF,\n    llvm::X86::SUB32mi_NF_ND,\n    llvm::X86::SUB32mr,\n    llvm::X86::SUB32mr_EVEX,\n    llvm::X86::SUB32mr_ND,\n    llvm::X86::SUB32mr_NF,\n    llvm::X86::SUB32mr_NF_ND,\n    llvm::X86::SUB32rm,\n    llvm::X86::SUB32rm_EVEX,\n    llvm::X86::SUB32rm_ND,\n    llvm::X86::SUB32rm_NF,\n    llvm::X86::SUB32rm_NF_ND,\n    llvm::X86::SUBR_F32m,\n    llvm::X86::SUBR_FI32m,\n    llvm::X86::SUBSSrm,\n    llvm::X86::SUBSSrm_Int,\n    llvm::X86::SUB_F32m,\n    llvm::X86::SUB_FI32m,\n    llvm::X86::T1MSKC32rm,\n    llvm::X86::TEST32mi,\n    llvm::X86::TEST32mr,\n    llvm::X86::TZCNT32rm,\n    llvm::X86::TZCNT32rm_EVEX,\n    llvm::X86::TZCNT32rm_NF,\n    llvm::X86::TZMSK32rm,\n    llvm::X86::UCOMISSrm,\n    llvm::X86::UCOMISSrm_Int,\n    llvm::X86::VADDSSZrm,\n    llvm::X86::VADDSSrm,\n    llvm::X86::VADDSSrm_Int,\n    llvm::X86::VBROADCASTSSYrm,\n    llvm::X86::VBROADCASTSSrm,\n    llvm::X86::VCMPSSrmi,\n    llvm::X86::VCMPSSrmi_Int,\n    llvm::X86::VCOMISSZrm,\n    llvm::X86::VCOMISSZrm_Int,\n    llvm::X86::VCOMISSrm,\n    llvm::X86::VCOMISSrm_Int,\n    llvm::X86::VCVTSI2SDZrm,\n    llvm::X86::VCVTSI2SDZrm_Int,\n    llvm::X86::VCVTSI2SDrm,\n    llvm::X86::VCVTSI2SDrm_Int,\n    llvm::X86::VCVTSI2SHZrm,\n    llvm::X86::VCVTSI2SHZrm_Int,\n    llvm::X86::VCVTSI2SSZrm,\n    llvm::X86::VCVTSI2SSZrm_Int,\n    llvm::X86::VCVTSI2SSrm,\n    llvm::X86::VCVTSI2SSrm_Int,\n    llvm::X86::VCVTSS2SDZrm,\n    llvm::X86::VCVTSS2SDrm,\n    llvm::X86::VCVTSS2SDrm_Int,\n    llvm::X86::VCVTSS2SHZrm,\n    llvm::X86::VCVTSS2SI64rm,\n    llvm::X86::VCVTSS2SI64rm_Int,\n    llvm::X86::VCVTSS2SIZrm_Int,\n    llvm::X86::VCVTSS2SIrm,\n    llvm::X86::VCVTSS2SIrm_Int,\n    llvm::X86::VCVTSS2USIZrm_Int,\n    llvm::X86::VCVTTSS2SI64rm,\n    llvm::X86::VCVTTSS2SI64rm_Int,\n    llvm::X86::VCVTTSS2SIrm,\n    llvm::X86::VCVTTSS2SIrm_Int,\n    llvm::X86::VCVTUSI2SDZrm,\n    llvm::X86::VCVTUSI2SDZrm_Int,\n    llvm::X86::VCVTUSI2SHZrm,\n    llvm::X86::VCVTUSI2SHZrm_Int,\n    llvm::X86::VCVTUSI2SSZrm,\n    llvm::X86::VCVTUSI2SSZrm_Int,\n    llvm::X86::VDIVSSZrm,\n    llvm::X86::VDIVSSrm,\n    llvm::X86::VDIVSSrm_Int,\n    llvm::X86::VFMADD132SSm,\n    llvm::X86::VFMADD132SSm_Int,\n    llvm::X86::VFMADD213SSm,\n    llvm::X86::VFMADD213SSm_Int,\n    llvm::X86::VFMADD231SSm,\n    llvm::X86::VFMADD231SSm_Int,\n    llvm::X86::VFMADDSS4mr,\n    llvm::X86::VFMADDSS4mr_Int,\n    llvm::X86::VFMADDSS4rm,\n    llvm::X86::VFMADDSS4rm_Int,\n    llvm::X86::VFMSUB132SSm,\n    llvm::X86::VFMSUB132SSm_Int,\n    llvm::X86::VFMSUB213SSm,\n    llvm::X86::VFMSUB213SSm_Int,\n    llvm::X86::VFMSUB231SSm,\n    llvm::X86::VFMSUB231SSm_Int,\n    llvm::X86::VFMSUBSS4mr,\n    llvm::X86::VFMSUBSS4mr_Int,\n    llvm::X86::VFMSUBSS4rm,\n    llvm::X86::VFMSUBSS4rm_Int,\n    llvm::X86::VFNMADD132SSm,\n    llvm::X86::VFNMADD132SSm_Int,\n    llvm::X86::VFNMADD213SSm,\n    llvm::X86::VFNMADD213SSm_Int,\n    llvm::X86::VFNMADD231SSm,\n    llvm::X86::VFNMADD231SSm_Int,\n    llvm::X86::VFNMADDSS4mr,\n    llvm::X86::VFNMADDSS4mr_Int,\n    llvm::X86::VFNMADDSS4rm,\n    llvm::X86::VFNMADDSS4rm_Int,\n    llvm::X86::VFNMSUB132SSm,\n    llvm::X86::VFNMSUB132SSm_Int,\n    llvm::X86::VFNMSUB213SSm,\n    llvm::X86::VFNMSUB213SSm_Int,\n    llvm::X86::VFNMSUB231SSm,\n    llvm::X86::VFNMSUB231SSm_Int,\n    llvm::X86::VFNMSUBSS4mr,\n    llvm::X86::VFNMSUBSS4mr_Int,\n    llvm::X86::VFNMSUBSS4rm,\n    llvm::X86::VFNMSUBSS4rm_Int,\n    llvm::X86::VINSERTPSrm,\n    llvm::X86::VLDMXCSR,\n    llvm::X86::VMAXCSSZrm,\n    llvm::X86::VMAXSSZrm,\n    llvm::X86::VMAXSSrm,\n    llvm::X86::VMAXSSrm_Int,\n    llvm::X86::VMINCSSZrm,\n    llvm::X86::VMINSSZrm,\n    llvm::X86::VMINSSrm,\n    llvm::X86::VMINSSrm_Int,\n    llvm::X86::VMOVDI2PDIrm,\n    llvm::X86::VMOVSSrm,\n    llvm::X86::VMOVSSrm_alt,\n    llvm::X86::VMULSSZrm,\n    llvm::X86::VMULSSrm,\n    llvm::X86::VMULSSrm_Int,\n    llvm::X86::VP2INTERSECTDZ128rmb,\n    llvm::X86::VP2INTERSECTDZ256rmb,\n    llvm::X86::VP2INTERSECTDZrmb,\n    llvm::X86::VPBROADCASTDYrm,\n    llvm::X86::VPBROADCASTDrm,\n    llvm::X86::VPINSRDrm,\n    llvm::X86::VPMOVSXBDrm,\n    llvm::X86::VPMOVSXBQYrm,\n    llvm::X86::VPMOVSXWQrm,\n    llvm::X86::VPMOVZXBDrm,\n    llvm::X86::VPMOVZXBQYrm,\n    llvm::X86::VPMOVZXWQrm,\n    llvm::X86::VRCPSSm,\n    llvm::X86::VRCPSSm_Int,\n    llvm::X86::VROUNDSSmi,\n    llvm::X86::VROUNDSSmi_Int,\n    llvm::X86::VRSQRTSSm,\n    llvm::X86::VRSQRTSSm_Int,\n    llvm::X86::VSQRTSSZm,\n    llvm::X86::VSQRTSSm,\n    llvm::X86::VSQRTSSm_Int,\n    llvm::X86::VSUBSSZrm,\n    llvm::X86::VSUBSSrm,\n    llvm::X86::VSUBSSrm_Int,\n    llvm::X86::VUCOMISSZrm,\n    llvm::X86::VUCOMISSZrm_Int,\n    llvm::X86::VUCOMISSrm,\n    llvm::X86::VUCOMISSrm_Int,\n    llvm::X86::XADD32rm,\n    llvm::X86::XCHG32rm,\n    llvm::X86::XOR32mi,\n    llvm::X86::XOR32mi8,\n    llvm::X86::XOR32mi8_EVEX,\n    llvm::X86::XOR32mi8_ND,\n    llvm::X86::XOR32mi8_NF,\n    llvm::X86::XOR32mi8_NF_ND,\n    llvm::X86::XOR32mi_EVEX,\n    llvm::X86::XOR32mi_ND,\n    llvm::X86::XOR32mi_NF,\n    llvm::X86::XOR32mi_NF_ND,\n    llvm::X86::XOR32mr,\n    llvm::X86::XOR32mr_EVEX,\n    llvm::X86::XOR32mr_ND,\n    llvm::X86::XOR32mr_NF,\n    llvm::X86::XOR32mr_NF_ND,\n    llvm::X86::XOR32rm,\n    llvm::X86::XOR32rm_EVEX,\n    llvm::X86::XOR32rm_ND,\n    llvm::X86::XOR32rm_NF,\n    llvm::X86::XOR32rm_NF_ND,\n    // clang-format on\n};\n\nconstexpr size_t READ_32_SIZE = sizeof(READ_32) / sizeof(unsigned);\n\nconstexpr unsigned READ_64[] = {\n    // clang-format off\n    llvm::X86::AADD64mr,\n    llvm::X86::AADD64mr_EVEX,\n    llvm::X86::AAND64mr,\n    llvm::X86::AAND64mr_EVEX,\n    llvm::X86::ADC64mi32,\n    llvm::X86::ADC64mi32_EVEX,\n    llvm::X86::ADC64mi32_ND,\n    llvm::X86::ADC64mi8,\n    llvm::X86::ADC64mi8_EVEX,\n    llvm::X86::ADC64mi8_ND,\n    llvm::X86::ADC64mr,\n    llvm::X86::ADC64mr_EVEX,\n    llvm::X86::ADC64mr_ND,\n    llvm::X86::ADC64rm,\n    llvm::X86::ADC64rm_EVEX,\n    llvm::X86::ADC64rm_ND,\n    llvm::X86::ADCX64rm,\n    llvm::X86::ADCX64rm_EVEX,\n    llvm::X86::ADCX64rm_ND,\n    llvm::X86::ADD64mi32,\n    llvm::X86::ADD64mi32_EVEX,\n    llvm::X86::ADD64mi32_ND,\n    llvm::X86::ADD64mi32_NF,\n    llvm::X86::ADD64mi32_NF_ND,\n    llvm::X86::ADD64mi8,\n    llvm::X86::ADD64mi8_EVEX,\n    llvm::X86::ADD64mi8_ND,\n    llvm::X86::ADD64mi8_NF,\n    llvm::X86::ADD64mi8_NF_ND,\n    llvm::X86::ADD64mr,\n    llvm::X86::ADD64mr_EVEX,\n    llvm::X86::ADD64mr_ND,\n    llvm::X86::ADD64mr_NF,\n    llvm::X86::ADD64mr_NF_ND,\n    llvm::X86::ADD64rm,\n    llvm::X86::ADD64rm_EVEX,\n    llvm::X86::ADD64rm_ND,\n    llvm::X86::ADD64rm_NF,\n    llvm::X86::ADD64rm_NF_ND,\n    llvm::X86::ADDSDrm,\n    llvm::X86::ADDSDrm_Int,\n    llvm::X86::ADD_F64m,\n    llvm::X86::ADOX64rm,\n    llvm::X86::ADOX64rm_EVEX,\n    llvm::X86::ADOX64rm_ND,\n    llvm::X86::AND64mi32,\n    llvm::X86::AND64mi32_EVEX,\n    llvm::X86::AND64mi32_ND,\n    llvm::X86::AND64mi32_NF,\n    llvm::X86::AND64mi32_NF_ND,\n    llvm::X86::AND64mi8,\n    llvm::X86::AND64mi8_EVEX,\n    llvm::X86::AND64mi8_ND,\n    llvm::X86::AND64mi8_NF,\n    llvm::X86::AND64mi8_NF_ND,\n    llvm::X86::AND64mr,\n    llvm::X86::AND64mr_EVEX,\n    llvm::X86::AND64mr_ND,\n    llvm::X86::AND64mr_NF,\n    llvm::X86::AND64mr_NF_ND,\n    llvm::X86::AND64rm,\n    llvm::X86::AND64rm_EVEX,\n    llvm::X86::AND64rm_ND,\n    llvm::X86::AND64rm_NF,\n    llvm::X86::AND64rm_NF_ND,\n    llvm::X86::ANDN64rm,\n    llvm::X86::ANDN64rm_EVEX,\n    llvm::X86::AOR64mr,\n    llvm::X86::AOR64mr_EVEX,\n    llvm::X86::AXOR64mr,\n    llvm::X86::AXOR64mr_EVEX,\n    llvm::X86::BEXTR64rm,\n    llvm::X86::BEXTR64rm_EVEX,\n    llvm::X86::BEXTR64rm_NF,\n    llvm::X86::BEXTRI64mi,\n    llvm::X86::BLCFILL64rm,\n    llvm::X86::BLCI64rm,\n    llvm::X86::BLCIC64rm,\n    llvm::X86::BLCMSK64rm,\n    llvm::X86::BLCS64rm,\n    llvm::X86::BLSFILL64rm,\n    llvm::X86::BLSI64rm,\n    llvm::X86::BLSI64rm_EVEX,\n    llvm::X86::BLSI64rm_NF,\n    llvm::X86::BLSIC64rm,\n    llvm::X86::BLSMSK64rm,\n    llvm::X86::BLSMSK64rm_EVEX,\n    llvm::X86::BLSMSK64rm_NF,\n    llvm::X86::BLSR64rm,\n    llvm::X86::BLSR64rm_EVEX,\n    llvm::X86::BLSR64rm_NF,\n    llvm::X86::BOUNDS32rm,\n    llvm::X86::BSF64rm,\n    llvm::X86::BSR64rm,\n    llvm::X86::BT64mi8,\n    llvm::X86::BT64mr,\n    llvm::X86::BTC64mi8,\n    llvm::X86::BTC64mr,\n    llvm::X86::BTR64mi8,\n    llvm::X86::BTR64mr,\n    llvm::X86::BTS64mi8,\n    llvm::X86::BTS64mr,\n    llvm::X86::BZHI64rm,\n    llvm::X86::BZHI64rm_EVEX,\n    llvm::X86::BZHI64rm_NF,\n    llvm::X86::CALL64m,\n    llvm::X86::CALL64m_NT,\n    llvm::X86::CCMP64mi32,\n    llvm::X86::CCMP64mi8,\n    llvm::X86::CCMP64mr,\n    llvm::X86::CCMP64rm,\n    llvm::X86::CFCMOV64rm,\n    llvm::X86::CFCMOV64rm_ND,\n    llvm::X86::CMOV64rm,\n    llvm::X86::CMOV64rm_ND,\n    llvm::X86::CMP64mi32,\n    llvm::X86::CMP64mi8,\n    llvm::X86::CMP64mr,\n    llvm::X86::CMP64rm,\n    llvm::X86::CMPCCXADDmr64,\n    llvm::X86::CMPCCXADDmr64_EVEX,\n    llvm::X86::CMPSDrmi,\n    llvm::X86::CMPSDrmi_Int,\n    llvm::X86::CMPSQ,\n    llvm::X86::CMPXCHG64rm,\n    llvm::X86::CMPXCHG8B,\n    llvm::X86::COMISDrm,\n    llvm::X86::COMISDrm_Int,\n    llvm::X86::CRC32r64m64,\n    llvm::X86::CRC32r64m64_EVEX,\n    llvm::X86::CTEST64mi32,\n    llvm::X86::CTEST64mr,\n    llvm::X86::CVTDQ2PDrm,\n    llvm::X86::CVTPS2PDrm,\n    llvm::X86::CVTSD2SI64rm,\n    llvm::X86::CVTSD2SI64rm_Int,\n    llvm::X86::CVTSD2SIrm,\n    llvm::X86::CVTSD2SIrm_Int,\n    llvm::X86::CVTSD2SSrm,\n    llvm::X86::CVTSD2SSrm_Int,\n    llvm::X86::CVTSI642SDrm,\n    llvm::X86::CVTSI642SDrm_Int,\n    llvm::X86::CVTSI642SSrm,\n    llvm::X86::CVTSI642SSrm_Int,\n    llvm::X86::CVTTSD2SI64rm,\n    llvm::X86::CVTTSD2SI64rm_Int,\n    llvm::X86::CVTTSD2SIrm,\n    llvm::X86::CVTTSD2SIrm_Int,\n    llvm::X86::DEC64m,\n    llvm::X86::DEC64m_EVEX,\n    llvm::X86::DEC64m_ND,\n    llvm::X86::DEC64m_NF,\n    llvm::X86::DEC64m_NF_ND,\n    llvm::X86::DIV64m,\n    llvm::X86::DIV64m_EVEX,\n    llvm::X86::DIV64m_NF,\n    llvm::X86::DIVR_F64m,\n    llvm::X86::DIVSDrm,\n    llvm::X86::DIVSDrm_Int,\n    llvm::X86::DIV_F64m,\n    llvm::X86::FCOM64m,\n    llvm::X86::FCOMP64m,\n    llvm::X86::IDIV64m,\n    llvm::X86::IDIV64m_EVEX,\n    llvm::X86::IDIV64m_NF,\n    llvm::X86::ILD_F64m,\n    llvm::X86::IMUL64m,\n    llvm::X86::IMUL64m_EVEX,\n    llvm::X86::IMUL64m_NF,\n    llvm::X86::IMUL64rm,\n    llvm::X86::IMUL64rm_EVEX,\n    llvm::X86::IMUL64rm_ND,\n    llvm::X86::IMUL64rm_NF,\n    llvm::X86::IMUL64rm_NF_ND,\n    llvm::X86::IMUL64rmi32,\n    llvm::X86::IMUL64rmi32_EVEX,\n    llvm::X86::IMUL64rmi32_NF,\n    llvm::X86::IMUL64rmi8,\n    llvm::X86::IMUL64rmi8_EVEX,\n    llvm::X86::IMUL64rmi8_NF,\n    llvm::X86::IMULZU64rmi32,\n    llvm::X86::IMULZU64rmi8,\n    llvm::X86::INC64m,\n    llvm::X86::INC64m_EVEX,\n    llvm::X86::INC64m_ND,\n    llvm::X86::INC64m_NF,\n    llvm::X86::INC64m_NF_ND,\n    llvm::X86::JMP64m,\n    llvm::X86::JMP64m_NT,\n    llvm::X86::JMP64m_REX,\n    llvm::X86::KMOVQkm,\n    llvm::X86::KMOVQkm_EVEX,\n    llvm::X86::LCMPXCHG64,\n    llvm::X86::LCMPXCHG8B,\n    llvm::X86::LD_F64m,\n    llvm::X86::LOCK_ADD64mi32,\n    llvm::X86::LOCK_ADD64mi8,\n    llvm::X86::LOCK_ADD64mr,\n    llvm::X86::LOCK_AND64mi32,\n    llvm::X86::LOCK_AND64mi8,\n    llvm::X86::LOCK_AND64mr,\n    llvm::X86::LOCK_BTC64m,\n    llvm::X86::LOCK_BTR64m,\n    llvm::X86::LOCK_BTS64m,\n    llvm::X86::LOCK_DEC64m,\n    llvm::X86::LOCK_INC64m,\n    llvm::X86::LOCK_OR64mi32,\n    llvm::X86::LOCK_OR64mi8,\n    llvm::X86::LOCK_OR64mr,\n    llvm::X86::LOCK_SUB64mi32,\n    llvm::X86::LOCK_SUB64mi8,\n    llvm::X86::LOCK_SUB64mr,\n    llvm::X86::LOCK_XOR64mi32,\n    llvm::X86::LOCK_XOR64mi8,\n    llvm::X86::LOCK_XOR64mr,\n    llvm::X86::LODSQ,\n    llvm::X86::LXADD64,\n    llvm::X86::LZCNT64rm,\n    llvm::X86::LZCNT64rm_EVEX,\n    llvm::X86::LZCNT64rm_NF,\n    llvm::X86::MAXSDrm,\n    llvm::X86::MAXSDrm_Int,\n    llvm::X86::MINSDrm,\n    llvm::X86::MINSDrm_Int,\n    llvm::X86::MMX_CVTPI2PDrm,\n    llvm::X86::MMX_CVTPI2PSrm,\n    llvm::X86::MMX_CVTPS2PIrm,\n    llvm::X86::MMX_CVTTPS2PIrm,\n    llvm::X86::MMX_MASKMOVQ,\n    llvm::X86::MMX_MASKMOVQ64,\n    llvm::X86::MMX_MOVQ64rm,\n    llvm::X86::MMX_PABSBrm,\n    llvm::X86::MMX_PABSDrm,\n    llvm::X86::MMX_PABSWrm,\n    llvm::X86::MMX_PACKSSDWrm,\n    llvm::X86::MMX_PACKSSWBrm,\n    llvm::X86::MMX_PACKUSWBrm,\n    llvm::X86::MMX_PADDBrm,\n    llvm::X86::MMX_PADDDrm,\n    llvm::X86::MMX_PADDQrm,\n    llvm::X86::MMX_PADDSBrm,\n    llvm::X86::MMX_PADDSWrm,\n    llvm::X86::MMX_PADDUSBrm,\n    llvm::X86::MMX_PADDUSWrm,\n    llvm::X86::MMX_PADDWrm,\n    llvm::X86::MMX_PALIGNRrmi,\n    llvm::X86::MMX_PANDNrm,\n    llvm::X86::MMX_PANDrm,\n    llvm::X86::MMX_PAVGBrm,\n    llvm::X86::MMX_PAVGWrm,\n    llvm::X86::MMX_PCMPEQBrm,\n    llvm::X86::MMX_PCMPEQDrm,\n    llvm::X86::MMX_PCMPEQWrm,\n    llvm::X86::MMX_PCMPGTBrm,\n    llvm::X86::MMX_PCMPGTDrm,\n    llvm::X86::MMX_PCMPGTWrm,\n    llvm::X86::MMX_PHADDDrm,\n    llvm::X86::MMX_PHADDSWrm,\n    llvm::X86::MMX_PHADDWrm,\n    llvm::X86::MMX_PHSUBDrm,\n    llvm::X86::MMX_PHSUBSWrm,\n    llvm::X86::MMX_PHSUBWrm,\n    llvm::X86::MMX_PMADDUBSWrm,\n    llvm::X86::MMX_PMADDWDrm,\n    llvm::X86::MMX_PMAXSWrm,\n    llvm::X86::MMX_PMAXUBrm,\n    llvm::X86::MMX_PMINSWrm,\n    llvm::X86::MMX_PMINUBrm,\n    llvm::X86::MMX_PMULHRSWrm,\n    llvm::X86::MMX_PMULHUWrm,\n    llvm::X86::MMX_PMULHWrm,\n    llvm::X86::MMX_PMULLWrm,\n    llvm::X86::MMX_PMULUDQrm,\n    llvm::X86::MMX_PORrm,\n    llvm::X86::MMX_PSADBWrm,\n    llvm::X86::MMX_PSHUFBrm,\n    llvm::X86::MMX_PSHUFWmi,\n    llvm::X86::MMX_PSIGNBrm,\n    llvm::X86::MMX_PSIGNDrm,\n    llvm::X86::MMX_PSIGNWrm,\n    llvm::X86::MMX_PSLLDrm,\n    llvm::X86::MMX_PSLLQrm,\n    llvm::X86::MMX_PSLLWrm,\n    llvm::X86::MMX_PSRADrm,\n    llvm::X86::MMX_PSRAWrm,\n    llvm::X86::MMX_PSRLDrm,\n    llvm::X86::MMX_PSRLQrm,\n    llvm::X86::MMX_PSRLWrm,\n    llvm::X86::MMX_PSUBBrm,\n    llvm::X86::MMX_PSUBDrm,\n    llvm::X86::MMX_PSUBQrm,\n    llvm::X86::MMX_PSUBSBrm,\n    llvm::X86::MMX_PSUBSWrm,\n    llvm::X86::MMX_PSUBUSBrm,\n    llvm::X86::MMX_PSUBUSWrm,\n    llvm::X86::MMX_PSUBWrm,\n    llvm::X86::MMX_PUNPCKHBWrm,\n    llvm::X86::MMX_PUNPCKHDQrm,\n    llvm::X86::MMX_PUNPCKHWDrm,\n    llvm::X86::MMX_PXORrm,\n    llvm::X86::MOV64ao32,\n    llvm::X86::MOV64ao64,\n    llvm::X86::MOV64rm,\n    llvm::X86::MOVBE64rm,\n    llvm::X86::MOVBE64rm_EVEX,\n    llvm::X86::MOVDDUPrm,\n    llvm::X86::MOVHPDrm,\n    llvm::X86::MOVHPSrm,\n    llvm::X86::MOVLPDrm,\n    llvm::X86::MOVLPSrm,\n    llvm::X86::MOVQI2PQIrm,\n    llvm::X86::MOVSDrm,\n    llvm::X86::MOVSDrm_alt,\n    llvm::X86::MOVSQ,\n    llvm::X86::MUL64m,\n    llvm::X86::MUL64m_EVEX,\n    llvm::X86::MUL64m_NF,\n    llvm::X86::MULSDrm,\n    llvm::X86::MULSDrm_Int,\n    llvm::X86::MULX64rm,\n    llvm::X86::MULX64rm_EVEX,\n    llvm::X86::MUL_F64m,\n    llvm::X86::NEG64m,\n    llvm::X86::NEG64m_EVEX,\n    llvm::X86::NEG64m_ND,\n    llvm::X86::NEG64m_NF,\n    llvm::X86::NEG64m_NF_ND,\n    llvm::X86::NOT64m,\n    llvm::X86::NOT64m_EVEX,\n    llvm::X86::NOT64m_ND,\n    llvm::X86::OR64mi32,\n    llvm::X86::OR64mi32_EVEX,\n    llvm::X86::OR64mi32_ND,\n    llvm::X86::OR64mi32_NF,\n    llvm::X86::OR64mi32_NF_ND,\n    llvm::X86::OR64mi8,\n    llvm::X86::OR64mi8_EVEX,\n    llvm::X86::OR64mi8_ND,\n    llvm::X86::OR64mi8_NF,\n    llvm::X86::OR64mi8_NF_ND,\n    llvm::X86::OR64mr,\n    llvm::X86::OR64mr_EVEX,\n    llvm::X86::OR64mr_ND,\n    llvm::X86::OR64mr_NF,\n    llvm::X86::OR64mr_NF_ND,\n    llvm::X86::OR64rm,\n    llvm::X86::OR64rm_EVEX,\n    llvm::X86::OR64rm_ND,\n    llvm::X86::OR64rm_NF,\n    llvm::X86::OR64rm_NF_ND,\n    llvm::X86::PAVGUSBrm,\n    llvm::X86::PDEP64rm,\n    llvm::X86::PDEP64rm_EVEX,\n    llvm::X86::PEXT64rm,\n    llvm::X86::PEXT64rm_EVEX,\n    llvm::X86::PF2IDrm,\n    llvm::X86::PF2IWrm,\n    llvm::X86::PFACCrm,\n    llvm::X86::PFADDrm,\n    llvm::X86::PFCMPEQrm,\n    llvm::X86::PFCMPGErm,\n    llvm::X86::PFCMPGTrm,\n    llvm::X86::PFMAXrm,\n    llvm::X86::PFMINrm,\n    llvm::X86::PFMULrm,\n    llvm::X86::PFNACCrm,\n    llvm::X86::PFPNACCrm,\n    llvm::X86::PFRCPIT1rm,\n    llvm::X86::PFRCPIT2rm,\n    llvm::X86::PFRCPrm,\n    llvm::X86::PFRSQIT1rm,\n    llvm::X86::PFRSQRTrm,\n    llvm::X86::PFSUBRrm,\n    llvm::X86::PFSUBrm,\n    llvm::X86::PI2FDrm,\n    llvm::X86::PI2FWrm,\n    llvm::X86::PINSRQrm,\n    llvm::X86::PMOVSXBWrm,\n    llvm::X86::PMOVSXDQrm,\n    llvm::X86::PMOVSXWDrm,\n    llvm::X86::PMOVZXBWrm,\n    llvm::X86::PMOVZXDQrm,\n    llvm::X86::PMOVZXWDrm,\n    llvm::X86::PMULHRWrm,\n    llvm::X86::POPCNT64rm,\n    llvm::X86::POPCNT64rm_EVEX,\n    llvm::X86::POPCNT64rm_NF,\n    llvm::X86::PSWAPDrm,\n    llvm::X86::PTWRITE64m,\n    llvm::X86::PUSH64rmm,\n    llvm::X86::RCL64m1,\n    llvm::X86::RCL64m1_EVEX,\n    llvm::X86::RCL64m1_ND,\n    llvm::X86::RCL64mCL,\n    llvm::X86::RCL64mCL_EVEX,\n    llvm::X86::RCL64mCL_ND,\n    llvm::X86::RCL64mi,\n    llvm::X86::RCL64mi_EVEX,\n    llvm::X86::RCL64mi_ND,\n    llvm::X86::RCR64m1,\n    llvm::X86::RCR64m1_EVEX,\n    llvm::X86::RCR64m1_ND,\n    llvm::X86::RCR64mCL,\n    llvm::X86::RCR64mCL_EVEX,\n    llvm::X86::RCR64mCL_ND,\n    llvm::X86::RCR64mi,\n    llvm::X86::RCR64mi_EVEX,\n    llvm::X86::RCR64mi_ND,\n    llvm::X86::ROL64m1,\n    llvm::X86::ROL64m1_EVEX,\n    llvm::X86::ROL64m1_ND,\n    llvm::X86::ROL64m1_NF,\n    llvm::X86::ROL64m1_NF_ND,\n    llvm::X86::ROL64mCL,\n    llvm::X86::ROL64mCL_EVEX,\n    llvm::X86::ROL64mCL_ND,\n    llvm::X86::ROL64mCL_NF,\n    llvm::X86::ROL64mCL_NF_ND,\n    llvm::X86::ROL64mi,\n    llvm::X86::ROL64mi_EVEX,\n    llvm::X86::ROL64mi_ND,\n    llvm::X86::ROL64mi_NF,\n    llvm::X86::ROL64mi_NF_ND,\n    llvm::X86::ROR64m1,\n    llvm::X86::ROR64m1_EVEX,\n    llvm::X86::ROR64m1_ND,\n    llvm::X86::ROR64m1_NF,\n    llvm::X86::ROR64m1_NF_ND,\n    llvm::X86::ROR64mCL,\n    llvm::X86::ROR64mCL_EVEX,\n    llvm::X86::ROR64mCL_ND,\n    llvm::X86::ROR64mCL_NF,\n    llvm::X86::ROR64mCL_NF_ND,\n    llvm::X86::ROR64mi,\n    llvm::X86::ROR64mi_EVEX,\n    llvm::X86::ROR64mi_ND,\n    llvm::X86::ROR64mi_NF,\n    llvm::X86::ROR64mi_NF_ND,\n    llvm::X86::RORX64mi,\n    llvm::X86::RORX64mi_EVEX,\n    llvm::X86::ROUNDSDmi,\n    llvm::X86::ROUNDSDmi_Int,\n    llvm::X86::SAR64m1,\n    llvm::X86::SAR64m1_EVEX,\n    llvm::X86::SAR64m1_ND,\n    llvm::X86::SAR64m1_NF,\n    llvm::X86::SAR64m1_NF_ND,\n    llvm::X86::SAR64mCL,\n    llvm::X86::SAR64mCL_EVEX,\n    llvm::X86::SAR64mCL_ND,\n    llvm::X86::SAR64mCL_NF,\n    llvm::X86::SAR64mCL_NF_ND,\n    llvm::X86::SAR64mi,\n    llvm::X86::SAR64mi_EVEX,\n    llvm::X86::SAR64mi_ND,\n    llvm::X86::SAR64mi_NF,\n    llvm::X86::SAR64mi_NF_ND,\n    llvm::X86::SARX64rm,\n    llvm::X86::SARX64rm_EVEX,\n    llvm::X86::SBB64mi32,\n    llvm::X86::SBB64mi32_EVEX,\n    llvm::X86::SBB64mi32_ND,\n    llvm::X86::SBB64mi8,\n    llvm::X86::SBB64mi8_EVEX,\n    llvm::X86::SBB64mi8_ND,\n    llvm::X86::SBB64mr,\n    llvm::X86::SBB64mr_EVEX,\n    llvm::X86::SBB64mr_ND,\n    llvm::X86::SBB64rm,\n    llvm::X86::SBB64rm_EVEX,\n    llvm::X86::SBB64rm_ND,\n    llvm::X86::SCASQ,\n    llvm::X86::SHL64m1,\n    llvm::X86::SHL64m1_EVEX,\n    llvm::X86::SHL64m1_ND,\n    llvm::X86::SHL64m1_NF,\n    llvm::X86::SHL64m1_NF_ND,\n    llvm::X86::SHL64mCL,\n    llvm::X86::SHL64mCL_EVEX,\n    llvm::X86::SHL64mCL_ND,\n    llvm::X86::SHL64mCL_NF,\n    llvm::X86::SHL64mCL_NF_ND,\n    llvm::X86::SHL64mi,\n    llvm::X86::SHL64mi_EVEX,\n    llvm::X86::SHL64mi_ND,\n    llvm::X86::SHL64mi_NF,\n    llvm::X86::SHL64mi_NF_ND,\n    llvm::X86::SHLD64mrCL,\n    llvm::X86::SHLD64mrCL_EVEX,\n    llvm::X86::SHLD64mrCL_ND,\n    llvm::X86::SHLD64mrCL_NF,\n    llvm::X86::SHLD64mrCL_NF_ND,\n    llvm::X86::SHLD64mri8,\n    llvm::X86::SHLD64mri8_EVEX,\n    llvm::X86::SHLD64mri8_ND,\n    llvm::X86::SHLD64mri8_NF,\n    llvm::X86::SHLD64mri8_NF_ND,\n    llvm::X86::SHLX64rm,\n    llvm::X86::SHLX64rm_EVEX,\n    llvm::X86::SHR64m1,\n    llvm::X86::SHR64m1_EVEX,\n    llvm::X86::SHR64m1_ND,\n    llvm::X86::SHR64m1_NF,\n    llvm::X86::SHR64m1_NF_ND,\n    llvm::X86::SHR64mCL,\n    llvm::X86::SHR64mCL_EVEX,\n    llvm::X86::SHR64mCL_ND,\n    llvm::X86::SHR64mCL_NF,\n    llvm::X86::SHR64mCL_NF_ND,\n    llvm::X86::SHR64mi,\n    llvm::X86::SHR64mi_EVEX,\n    llvm::X86::SHR64mi_ND,\n    llvm::X86::SHR64mi_NF,\n    llvm::X86::SHR64mi_NF_ND,\n    llvm::X86::SHRD64mrCL,\n    llvm::X86::SHRD64mrCL_EVEX,\n    llvm::X86::SHRD64mrCL_ND,\n    llvm::X86::SHRD64mrCL_NF,\n    llvm::X86::SHRD64mrCL_NF_ND,\n    llvm::X86::SHRD64mri8,\n    llvm::X86::SHRD64mri8_EVEX,\n    llvm::X86::SHRD64mri8_ND,\n    llvm::X86::SHRD64mri8_NF,\n    llvm::X86::SHRD64mri8_NF_ND,\n    llvm::X86::SHRX64rm,\n    llvm::X86::SHRX64rm_EVEX,\n    llvm::X86::SQRTSDm,\n    llvm::X86::SQRTSDm_Int,\n    llvm::X86::SUB64mi32,\n    llvm::X86::SUB64mi32_EVEX,\n    llvm::X86::SUB64mi32_ND,\n    llvm::X86::SUB64mi32_NF,\n    llvm::X86::SUB64mi32_NF_ND,\n    llvm::X86::SUB64mi8,\n    llvm::X86::SUB64mi8_EVEX,\n    llvm::X86::SUB64mi8_ND,\n    llvm::X86::SUB64mi8_NF,\n    llvm::X86::SUB64mi8_NF_ND,\n    llvm::X86::SUB64mr,\n    llvm::X86::SUB64mr_EVEX,\n    llvm::X86::SUB64mr_ND,\n    llvm::X86::SUB64mr_NF,\n    llvm::X86::SUB64mr_NF_ND,\n    llvm::X86::SUB64rm,\n    llvm::X86::SUB64rm_EVEX,\n    llvm::X86::SUB64rm_ND,\n    llvm::X86::SUB64rm_NF,\n    llvm::X86::SUB64rm_NF_ND,\n    llvm::X86::SUBR_F64m,\n    llvm::X86::SUBSDrm,\n    llvm::X86::SUBSDrm_Int,\n    llvm::X86::SUB_F64m,\n    llvm::X86::T1MSKC64rm,\n    llvm::X86::TEST64mi32,\n    llvm::X86::TEST64mr,\n    llvm::X86::TZCNT64rm,\n    llvm::X86::TZCNT64rm_EVEX,\n    llvm::X86::TZCNT64rm_NF,\n    llvm::X86::TZMSK64rm,\n    llvm::X86::UCOMISDrm,\n    llvm::X86::UCOMISDrm_Int,\n    llvm::X86::VADDSDZrm,\n    llvm::X86::VADDSDrm,\n    llvm::X86::VADDSDrm_Int,\n    llvm::X86::VBROADCASTSDYrm,\n    llvm::X86::VCMPSDrmi,\n    llvm::X86::VCMPSDrmi_Int,\n    llvm::X86::VCOMISDZrm,\n    llvm::X86::VCOMISDZrm_Int,\n    llvm::X86::VCOMISDrm,\n    llvm::X86::VCOMISDrm_Int,\n    llvm::X86::VCVTDQ2PDrm,\n    llvm::X86::VCVTPH2PSrm,\n    llvm::X86::VCVTPS2PDrm,\n    llvm::X86::VCVTSD2SHZrm,\n    llvm::X86::VCVTSD2SI64Zrm_Int,\n    llvm::X86::VCVTSD2SI64rm,\n    llvm::X86::VCVTSD2SI64rm_Int,\n    llvm::X86::VCVTSD2SIZrm_Int,\n    llvm::X86::VCVTSD2SIrm,\n    llvm::X86::VCVTSD2SIrm_Int,\n    llvm::X86::VCVTSD2SSZrm,\n    llvm::X86::VCVTSD2SSrm,\n    llvm::X86::VCVTSD2SSrm_Int,\n    llvm::X86::VCVTSD2USI64Zrm_Int,\n    llvm::X86::VCVTSD2USIZrm_Int,\n    llvm::X86::VCVTSI642SDZrm,\n    llvm::X86::VCVTSI642SDZrm_Int,\n    llvm::X86::VCVTSI642SDrm,\n    llvm::X86::VCVTSI642SDrm_Int,\n    llvm::X86::VCVTSI642SHZrm,\n    llvm::X86::VCVTSI642SHZrm_Int,\n    llvm::X86::VCVTSI642SSZrm,\n    llvm::X86::VCVTSI642SSZrm_Int,\n    llvm::X86::VCVTSI642SSrm,\n    llvm::X86::VCVTSI642SSrm_Int,\n    llvm::X86::VCVTSS2SI64Zrm_Int,\n    llvm::X86::VCVTSS2USI64Zrm_Int,\n    llvm::X86::VCVTTSD2SI64rm,\n    llvm::X86::VCVTTSD2SI64rm_Int,\n    llvm::X86::VCVTTSD2SIrm,\n    llvm::X86::VCVTTSD2SIrm_Int,\n    llvm::X86::VCVTUSI642SDZrm,\n    llvm::X86::VCVTUSI642SDZrm_Int,\n    llvm::X86::VCVTUSI642SHZrm,\n    llvm::X86::VCVTUSI642SHZrm_Int,\n    llvm::X86::VCVTUSI642SSZrm,\n    llvm::X86::VCVTUSI642SSZrm_Int,\n    llvm::X86::VDIVSDZrm,\n    llvm::X86::VDIVSDrm,\n    llvm::X86::VDIVSDrm_Int,\n    llvm::X86::VFMADD132SDm,\n    llvm::X86::VFMADD132SDm_Int,\n    llvm::X86::VFMADD213SDm,\n    llvm::X86::VFMADD213SDm_Int,\n    llvm::X86::VFMADD231SDm,\n    llvm::X86::VFMADD231SDm_Int,\n    llvm::X86::VFMADDSD4mr,\n    llvm::X86::VFMADDSD4mr_Int,\n    llvm::X86::VFMADDSD4rm,\n    llvm::X86::VFMADDSD4rm_Int,\n    llvm::X86::VFMSUB132SDm,\n    llvm::X86::VFMSUB132SDm_Int,\n    llvm::X86::VFMSUB213SDm,\n    llvm::X86::VFMSUB213SDm_Int,\n    llvm::X86::VFMSUB231SDm,\n    llvm::X86::VFMSUB231SDm_Int,\n    llvm::X86::VFMSUBSD4mr,\n    llvm::X86::VFMSUBSD4mr_Int,\n    llvm::X86::VFMSUBSD4rm,\n    llvm::X86::VFMSUBSD4rm_Int,\n    llvm::X86::VFNMADD132SDm,\n    llvm::X86::VFNMADD132SDm_Int,\n    llvm::X86::VFNMADD213SDm,\n    llvm::X86::VFNMADD213SDm_Int,\n    llvm::X86::VFNMADD231SDm,\n    llvm::X86::VFNMADD231SDm_Int,\n    llvm::X86::VFNMADDSD4mr,\n    llvm::X86::VFNMADDSD4mr_Int,\n    llvm::X86::VFNMADDSD4rm,\n    llvm::X86::VFNMADDSD4rm_Int,\n    llvm::X86::VFNMSUB132SDm,\n    llvm::X86::VFNMSUB132SDm_Int,\n    llvm::X86::VFNMSUB213SDm,\n    llvm::X86::VFNMSUB213SDm_Int,\n    llvm::X86::VFNMSUB231SDm,\n    llvm::X86::VFNMSUB231SDm_Int,\n    llvm::X86::VFNMSUBSD4mr,\n    llvm::X86::VFNMSUBSD4mr_Int,\n    llvm::X86::VFNMSUBSD4rm,\n    llvm::X86::VFNMSUBSD4rm_Int,\n    llvm::X86::VMAXCSDZrm,\n    llvm::X86::VMAXSDrm,\n    llvm::X86::VMAXSDrm_Int,\n    llvm::X86::VMAXSDZrm,\n    llvm::X86::VMINCSDZrm,\n    llvm::X86::VMINSDrm,\n    llvm::X86::VMINSDrm_Int,\n    llvm::X86::VMINSDZrm,\n    llvm::X86::VMOVDDUPrm,\n    llvm::X86::VMOVHPDrm,\n    llvm::X86::VMOVHPSrm,\n    llvm::X86::VMOVLPDrm,\n    llvm::X86::VMOVLPSrm,\n    llvm::X86::VMOVQI2PQIrm,\n    llvm::X86::VMOVSDrm,\n    llvm::X86::VMOVSDrm_alt,\n    llvm::X86::VMULSDZrm,\n    llvm::X86::VMULSDrm,\n    llvm::X86::VMULSDrm_Int,\n    llvm::X86::VP2INTERSECTQZrmb,\n    llvm::X86::VP2INTERSECTQZ128rmb,\n    llvm::X86::VP2INTERSECTQZ256rmb,\n    llvm::X86::VPBROADCASTQYrm,\n    llvm::X86::VPBROADCASTQrm,\n    llvm::X86::VPINSRQrm,\n    llvm::X86::VPMOVSXBDYrm,\n    llvm::X86::VPMOVSXBWrm,\n    llvm::X86::VPMOVSXDQrm,\n    llvm::X86::VPMOVSXWDrm,\n    llvm::X86::VPMOVSXWQYrm,\n    llvm::X86::VPMOVZXBDYrm,\n    llvm::X86::VPMOVZXBWrm,\n    llvm::X86::VPMOVZXDQrm,\n    llvm::X86::VPMOVZXWDrm,\n    llvm::X86::VPMOVZXWQYrm,\n    llvm::X86::VROUNDSDmi,\n    llvm::X86::VROUNDSDmi_Int,\n    llvm::X86::VSQRTSDZm,\n    llvm::X86::VSQRTSDm,\n    llvm::X86::VSQRTSDm_Int,\n    llvm::X86::VSUBSDZrm,\n    llvm::X86::VSUBSDrm,\n    llvm::X86::VSUBSDrm_Int,\n    llvm::X86::VUCOMISDZrm,\n    llvm::X86::VUCOMISDZrm_Int,\n    llvm::X86::VUCOMISDrm,\n    llvm::X86::VUCOMISDrm_Int,\n    llvm::X86::XADD64rm,\n    llvm::X86::XCHG64rm,\n    llvm::X86::XOR64mi32,\n    llvm::X86::XOR64mi32_EVEX,\n    llvm::X86::XOR64mi32_ND,\n    llvm::X86::XOR64mi32_NF,\n    llvm::X86::XOR64mi32_NF_ND,\n    llvm::X86::XOR64mi8,\n    llvm::X86::XOR64mi8_EVEX,\n    llvm::X86::XOR64mi8_ND,\n    llvm::X86::XOR64mi8_NF,\n    llvm::X86::XOR64mi8_NF_ND,\n    llvm::X86::XOR64mr,\n    llvm::X86::XOR64mr_EVEX,\n    llvm::X86::XOR64mr_ND,\n    llvm::X86::XOR64mr_NF,\n    llvm::X86::XOR64mr_NF_ND,\n    llvm::X86::XOR64rm,\n    llvm::X86::XOR64rm_EVEX,\n    llvm::X86::XOR64rm_ND,\n    llvm::X86::XOR64rm_NF,\n    llvm::X86::XOR64rm_NF_ND,\n    // clang-format on\n};\n\nconstexpr size_t READ_64_SIZE = sizeof(READ_64) / sizeof(unsigned);\n\nconstexpr unsigned READ_80[] = {\n    // clang-format off\n    llvm::X86::FBLDm,\n    llvm::X86::LD_F80m,\n    // clang-format on\n};\n\nconstexpr size_t READ_80_SIZE = sizeof(READ_80) / sizeof(unsigned);\n\nconstexpr unsigned READ_128[] = {\n    // clang-format off\n    llvm::X86::ADDPDrm,\n    llvm::X86::ADDPSrm,\n    llvm::X86::ADDSUBPDrm,\n    llvm::X86::ADDSUBPSrm,\n    llvm::X86::AESDECLASTrm,\n    llvm::X86::AESDECrm,\n    llvm::X86::AESENCLASTrm,\n    llvm::X86::AESENCrm,\n    llvm::X86::AESIMCrm,\n    llvm::X86::AESKEYGENASSIST128rm,\n    llvm::X86::ANDNPDrm,\n    llvm::X86::ANDNPSrm,\n    llvm::X86::ANDPDrm,\n    llvm::X86::ANDPSrm,\n    llvm::X86::BLENDPDrmi,\n    llvm::X86::BLENDPSrmi,\n    llvm::X86::BLENDVPDrm0,\n    llvm::X86::BLENDVPSrm0,\n    llvm::X86::CMPPDrmi,\n    llvm::X86::CMPPSrmi,\n    llvm::X86::CMPXCHG16B,\n    llvm::X86::CVTDQ2PSrm,\n    llvm::X86::CVTPD2DQrm,\n    llvm::X86::CVTPD2PSrm,\n    llvm::X86::CVTPS2DQrm,\n    llvm::X86::CVTTPD2DQrm,\n    llvm::X86::CVTTPS2DQrm,\n    llvm::X86::DIVPDrm,\n    llvm::X86::DIVPSrm,\n    llvm::X86::DPPDrmi,\n    llvm::X86::DPPSrmi,\n    llvm::X86::GF2P8AFFINEINVQBrmi,\n    llvm::X86::GF2P8AFFINEQBrmi,\n    llvm::X86::GF2P8MULBrm,\n    llvm::X86::HADDPDrm,\n    llvm::X86::HADDPSrm,\n    llvm::X86::HSUBPDrm,\n    llvm::X86::HSUBPSrm,\n    llvm::X86::LCMPXCHG16B,\n    llvm::X86::LDDQUrm,\n    llvm::X86::MASKMOVDQU,\n    llvm::X86::MASKMOVDQU64,\n    llvm::X86::MAXPDrm,\n    llvm::X86::MAXPSrm,\n    llvm::X86::MINPDrm,\n    llvm::X86::MINPSrm,\n    llvm::X86::MMX_CVTPD2PIrm,\n    llvm::X86::MMX_CVTTPD2PIrm,\n    llvm::X86::MOVAPDrm,\n    llvm::X86::MOVAPSrm,\n    llvm::X86::MOVDQArm,\n    llvm::X86::MOVDQUrm,\n    llvm::X86::MOVNTDQArm,\n    llvm::X86::MOVSHDUPrm,\n    llvm::X86::MOVSLDUPrm,\n    llvm::X86::MOVUPDrm,\n    llvm::X86::MOVUPSrm,\n    llvm::X86::MPSADBWrmi,\n    llvm::X86::MULPDrm,\n    llvm::X86::MULPSrm,\n    llvm::X86::ORPDrm,\n    llvm::X86::ORPSrm,\n    llvm::X86::PABSBrm,\n    llvm::X86::PABSDrm,\n    llvm::X86::PABSWrm,\n    llvm::X86::PACKSSDWrm,\n    llvm::X86::PACKSSWBrm,\n    llvm::X86::PACKUSDWrm,\n    llvm::X86::PACKUSWBrm,\n    llvm::X86::PADDBrm,\n    llvm::X86::PADDDrm,\n    llvm::X86::PADDQrm,\n    llvm::X86::PADDSBrm,\n    llvm::X86::PADDSWrm,\n    llvm::X86::PADDUSBrm,\n    llvm::X86::PADDUSWrm,\n    llvm::X86::PADDWrm,\n    llvm::X86::PALIGNRrmi,\n    llvm::X86::PANDNrm,\n    llvm::X86::PANDrm,\n    llvm::X86::PAVGBrm,\n    llvm::X86::PAVGWrm,\n    llvm::X86::PBLENDVBrm0,\n    llvm::X86::PBLENDWrmi,\n    llvm::X86::PCLMULQDQrmi,\n    llvm::X86::PCMPEQBrm,\n    llvm::X86::PCMPEQDrm,\n    llvm::X86::PCMPEQQrm,\n    llvm::X86::PCMPEQWrm,\n    llvm::X86::PCMPESTRIrmi,\n    llvm::X86::PCMPESTRMrmi,\n    llvm::X86::PCMPGTBrm,\n    llvm::X86::PCMPGTDrm,\n    llvm::X86::PCMPGTQrm,\n    llvm::X86::PCMPGTWrm,\n    llvm::X86::PCMPISTRIrmi,\n    llvm::X86::PCMPISTRMrmi,\n    llvm::X86::PHADDDrm,\n    llvm::X86::PHADDSWrm,\n    llvm::X86::PHADDWrm,\n    llvm::X86::PHMINPOSUWrm,\n    llvm::X86::PHSUBDrm,\n    llvm::X86::PHSUBSWrm,\n    llvm::X86::PHSUBWrm,\n    llvm::X86::PMADDUBSWrm,\n    llvm::X86::PMADDWDrm,\n    llvm::X86::PMAXSBrm,\n    llvm::X86::PMAXSDrm,\n    llvm::X86::PMAXSWrm,\n    llvm::X86::PMAXUBrm,\n    llvm::X86::PMAXUDrm,\n    llvm::X86::PMAXUWrm,\n    llvm::X86::PMINSBrm,\n    llvm::X86::PMINSDrm,\n    llvm::X86::PMINSWrm,\n    llvm::X86::PMINUBrm,\n    llvm::X86::PMINUDrm,\n    llvm::X86::PMINUWrm,\n    llvm::X86::PMULDQrm,\n    llvm::X86::PMULHRSWrm,\n    llvm::X86::PMULHUWrm,\n    llvm::X86::PMULHWrm,\n    llvm::X86::PMULLDrm,\n    llvm::X86::PMULLWrm,\n    llvm::X86::PMULUDQrm,\n    llvm::X86::PORrm,\n    llvm::X86::PSADBWrm,\n    llvm::X86::PSHUFBrm,\n    llvm::X86::PSHUFDmi,\n    llvm::X86::PSHUFHWmi,\n    llvm::X86::PSHUFLWmi,\n    llvm::X86::PSIGNBrm,\n    llvm::X86::PSIGNDrm,\n    llvm::X86::PSIGNWrm,\n    llvm::X86::PSLLDrm,\n    llvm::X86::PSLLQrm,\n    llvm::X86::PSLLWrm,\n    llvm::X86::PSRADrm,\n    llvm::X86::PSRAWrm,\n    llvm::X86::PSRLDrm,\n    llvm::X86::PSRLQrm,\n    llvm::X86::PSRLWrm,\n    llvm::X86::PSUBBrm,\n    llvm::X86::PSUBDrm,\n    llvm::X86::PSUBQrm,\n    llvm::X86::PSUBSBrm,\n    llvm::X86::PSUBSWrm,\n    llvm::X86::PSUBUSBrm,\n    llvm::X86::PSUBUSWrm,\n    llvm::X86::PSUBWrm,\n    llvm::X86::PTESTrm,\n    llvm::X86::PUNPCKHBWrm,\n    llvm::X86::PUNPCKHDQrm,\n    llvm::X86::PUNPCKHQDQrm,\n    llvm::X86::PUNPCKHWDrm,\n    llvm::X86::PUNPCKLBWrm,\n    llvm::X86::PUNPCKLDQrm,\n    llvm::X86::PUNPCKLQDQrm,\n    llvm::X86::PUNPCKLWDrm,\n    llvm::X86::PXORrm,\n    llvm::X86::RCPPSm,\n    llvm::X86::ROUNDPDmi,\n    llvm::X86::ROUNDPSmi,\n    llvm::X86::RSQRTPSm,\n    llvm::X86::SHA1MSG1rm,\n    llvm::X86::SHA1MSG2rm,\n    llvm::X86::SHA1NEXTErm,\n    llvm::X86::SHA1RNDS4rmi,\n    llvm::X86::SHA256MSG1rm,\n    llvm::X86::SHA256MSG2rm,\n    llvm::X86::SHA256RNDS2rm,\n    llvm::X86::SHUFPDrmi,\n    llvm::X86::SHUFPSrmi,\n    llvm::X86::SQRTPDm,\n    llvm::X86::SQRTPSm,\n    llvm::X86::SUBPDrm,\n    llvm::X86::SUBPSrm,\n    llvm::X86::UNPCKHPDrm,\n    llvm::X86::UNPCKHPSrm,\n    llvm::X86::UNPCKLPDrm,\n    llvm::X86::UNPCKLPSrm,\n    llvm::X86::VADDPDrm,\n    llvm::X86::VADDPSrm,\n    llvm::X86::VADDSUBPDrm,\n    llvm::X86::VADDSUBPSrm,\n    llvm::X86::VAESDECLASTrm,\n    llvm::X86::VAESDECLASTZ128rm,\n    llvm::X86::VAESDECrm,\n    llvm::X86::VAESDECZ128rm,\n    llvm::X86::VAESENCLASTrm,\n    llvm::X86::VAESENCLASTZ128rm,\n    llvm::X86::VAESENCrm,\n    llvm::X86::VAESENCZ128rm,\n    llvm::X86::VAESIMCrm,\n    llvm::X86::VAESKEYGENASSIST128rm,\n    llvm::X86::VANDNPDrm,\n    llvm::X86::VANDNPSrm,\n    llvm::X86::VANDPDrm,\n    llvm::X86::VANDPSrm,\n    llvm::X86::VBLENDPDrmi,\n    llvm::X86::VBLENDPSrmi,\n    llvm::X86::VBLENDVPDrmr,\n    llvm::X86::VBLENDVPSrmr,\n    llvm::X86::VBROADCASTF128rm,\n    llvm::X86::VBROADCASTI128rm,\n    llvm::X86::VCMPPDrmi,\n    llvm::X86::VCMPPSrmi,\n    llvm::X86::VCVTDQ2PDYrm,\n    llvm::X86::VCVTDQ2PSrm,\n    llvm::X86::VCVTNEEBF162PSrm,\n    llvm::X86::VCVTNEEPH2PSrm,\n    llvm::X86::VCVTNEOBF162PSrm,\n    llvm::X86::VCVTNEOPH2PSrm,\n    llvm::X86::VCVTNEPS2BF16rm,\n    llvm::X86::VCVTPD2DQrm,\n    llvm::X86::VCVTPD2PSrm,\n    llvm::X86::VCVTPH2PSYrm,\n    llvm::X86::VCVTPS2DQrm,\n    llvm::X86::VCVTPS2PDYrm,\n    llvm::X86::VCVTTPD2DQrm,\n    llvm::X86::VCVTTPS2DQrm,\n    llvm::X86::VDIVPDrm,\n    llvm::X86::VDIVPSrm,\n    llvm::X86::VDPPDrmi,\n    llvm::X86::VDPPSrmi,\n    llvm::X86::VFMADD132PDm,\n    llvm::X86::VFMADD132PSm,\n    llvm::X86::VFMADD213PDm,\n    llvm::X86::VFMADD213PSm,\n    llvm::X86::VFMADD231PDm,\n    llvm::X86::VFMADD231PSm,\n    llvm::X86::VFMADDPD4mr,\n    llvm::X86::VFMADDPD4rm,\n    llvm::X86::VFMADDPS4mr,\n    llvm::X86::VFMADDPS4rm,\n    llvm::X86::VFMADDSUB132PDm,\n    llvm::X86::VFMADDSUB132PSm,\n    llvm::X86::VFMADDSUB213PDm,\n    llvm::X86::VFMADDSUB213PSm,\n    llvm::X86::VFMADDSUB231PDm,\n    llvm::X86::VFMADDSUB231PSm,\n    llvm::X86::VFMADDSUBPD4mr,\n    llvm::X86::VFMADDSUBPD4rm,\n    llvm::X86::VFMADDSUBPS4mr,\n    llvm::X86::VFMADDSUBPS4rm,\n    llvm::X86::VFMSUB132PDm,\n    llvm::X86::VFMSUB132PSm,\n    llvm::X86::VFMSUB213PDm,\n    llvm::X86::VFMSUB213PSm,\n    llvm::X86::VFMSUB231PDm,\n    llvm::X86::VFMSUB231PSm,\n    llvm::X86::VFMSUBADD132PDm,\n    llvm::X86::VFMSUBADD132PSm,\n    llvm::X86::VFMSUBADD213PDm,\n    llvm::X86::VFMSUBADD213PSm,\n    llvm::X86::VFMSUBADD231PDm,\n    llvm::X86::VFMSUBADD231PSm,\n    llvm::X86::VFMSUBADDPD4mr,\n    llvm::X86::VFMSUBADDPD4rm,\n    llvm::X86::VFMSUBADDPS4mr,\n    llvm::X86::VFMSUBADDPS4rm,\n    llvm::X86::VFMSUBPD4mr,\n    llvm::X86::VFMSUBPD4rm,\n    llvm::X86::VFMSUBPS4mr,\n    llvm::X86::VFMSUBPS4rm,\n    llvm::X86::VFNMADD132PDm,\n    llvm::X86::VFNMADD132PSm,\n    llvm::X86::VFNMADD213PDm,\n    llvm::X86::VFNMADD213PSm,\n    llvm::X86::VFNMADD231PDm,\n    llvm::X86::VFNMADD231PSm,\n    llvm::X86::VFNMADDPD4mr,\n    llvm::X86::VFNMADDPD4rm,\n    llvm::X86::VFNMADDPS4mr,\n    llvm::X86::VFNMADDPS4rm,\n    llvm::X86::VFNMSUB132PDm,\n    llvm::X86::VFNMSUB132PSm,\n    llvm::X86::VFNMSUB213PDm,\n    llvm::X86::VFNMSUB213PSm,\n    llvm::X86::VFNMSUB231PDm,\n    llvm::X86::VFNMSUB231PSm,\n    llvm::X86::VFNMSUBPD4mr,\n    llvm::X86::VFNMSUBPD4rm,\n    llvm::X86::VFNMSUBPS4mr,\n    llvm::X86::VFNMSUBPS4rm,\n    llvm::X86::VGF2P8AFFINEINVQBrmi,\n    llvm::X86::VGF2P8AFFINEQBrmi,\n    llvm::X86::VGF2P8MULBrm,\n    llvm::X86::VHADDPDrm,\n    llvm::X86::VHADDPSrm,\n    llvm::X86::VHSUBPDrm,\n    llvm::X86::VHSUBPSrm,\n    llvm::X86::VINSERTF128rm,\n    llvm::X86::VINSERTI128rm,\n    llvm::X86::VLDDQUrm,\n    llvm::X86::VMASKMOVDQU,\n    llvm::X86::VMASKMOVDQU64,\n    llvm::X86::VMASKMOVPDmr,\n    llvm::X86::VMASKMOVPDrm,\n    llvm::X86::VMASKMOVPSmr,\n    llvm::X86::VMASKMOVPSrm,\n    llvm::X86::VMAXPDrm,\n    llvm::X86::VMAXPSrm,\n    llvm::X86::VMINPDrm,\n    llvm::X86::VMINPSrm,\n    llvm::X86::VMOVAPDrm,\n    llvm::X86::VMOVAPSrm,\n    llvm::X86::VMOVDQArm,\n    llvm::X86::VMOVDQUrm,\n    llvm::X86::VMOVNTDQArm,\n    llvm::X86::VMOVSHDUPrm,\n    llvm::X86::VMOVSLDUPrm,\n    llvm::X86::VMOVUPDrm,\n    llvm::X86::VMOVUPSrm,\n    llvm::X86::VMPSADBWrmi,\n    llvm::X86::VMULPDrm,\n    llvm::X86::VMULPSrm,\n    llvm::X86::VORPDrm,\n    llvm::X86::VORPSrm,\n    llvm::X86::VP2INTERSECTDZ128rm,\n    llvm::X86::VP2INTERSECTQZ128rm,\n    llvm::X86::VPABSBrm,\n    llvm::X86::VPABSDrm,\n    llvm::X86::VPABSWrm,\n    llvm::X86::VPACKSSDWrm,\n    llvm::X86::VPACKSSWBrm,\n    llvm::X86::VPACKUSDWrm,\n    llvm::X86::VPACKUSWBrm,\n    llvm::X86::VPADDBrm,\n    llvm::X86::VPADDDrm,\n    llvm::X86::VPADDQrm,\n    llvm::X86::VPADDSBrm,\n    llvm::X86::VPADDSWrm,\n    llvm::X86::VPADDUSBrm,\n    llvm::X86::VPADDUSWrm,\n    llvm::X86::VPADDWrm,\n    llvm::X86::VPALIGNRrmi,\n    llvm::X86::VPANDNrm,\n    llvm::X86::VPANDrm,\n    llvm::X86::VPAVGBrm,\n    llvm::X86::VPAVGWrm,\n    llvm::X86::VPBLENDDrmi,\n    llvm::X86::VPBLENDVBrmr,\n    llvm::X86::VPBLENDWrmi,\n    llvm::X86::VPCLMULQDQZ128rmi,\n    llvm::X86::VPCLMULQDQrmi,\n    llvm::X86::VPCMPEQBrm,\n    llvm::X86::VPCMPEQDrm,\n    llvm::X86::VPCMPEQQrm,\n    llvm::X86::VPCMPEQWrm,\n    llvm::X86::VPCMPESTRIrmi,\n    llvm::X86::VPCMPESTRMrmi,\n    llvm::X86::VPCMPGTBrm,\n    llvm::X86::VPCMPGTDrm,\n    llvm::X86::VPCMPGTQrm,\n    llvm::X86::VPCMPGTWrm,\n    llvm::X86::VPCMPISTRIrmi,\n    llvm::X86::VPCMPISTRMrmi,\n    llvm::X86::VPDPBSSDSrm,\n    llvm::X86::VPDPBSSDrm,\n    llvm::X86::VPDPBSUDSrm,\n    llvm::X86::VPDPBSUDrm,\n    llvm::X86::VPDPBUSDSrm,\n    llvm::X86::VPDPBUSDrm,\n    llvm::X86::VPDPBUUDSrm,\n    llvm::X86::VPDPBUUDrm,\n    llvm::X86::VPDPWSSDSrm,\n    llvm::X86::VPDPWSSDrm,\n    llvm::X86::VPDPWSUDSrm,\n    llvm::X86::VPDPWSUDrm,\n    llvm::X86::VPDPWUSDSrm,\n    llvm::X86::VPDPWUSDrm,\n    llvm::X86::VPDPWUUDSrm,\n    llvm::X86::VPDPWUUDrm,\n    llvm::X86::VPERMIL2PDmr,\n    llvm::X86::VPERMIL2PDrm,\n    llvm::X86::VPERMIL2PSmr,\n    llvm::X86::VPERMIL2PSrm,\n    llvm::X86::VPERMILPDmi,\n    llvm::X86::VPERMILPDrm,\n    llvm::X86::VPERMILPSmi,\n    llvm::X86::VPERMILPSrm,\n    llvm::X86::VPHADDDrm,\n    llvm::X86::VPHADDSWrm,\n    llvm::X86::VPHADDWrm,\n    llvm::X86::VPHMINPOSUWrm,\n    llvm::X86::VPHSUBDrm,\n    llvm::X86::VPHSUBSWrm,\n    llvm::X86::VPHSUBWrm,\n    llvm::X86::VPMADD52HUQrm,\n    llvm::X86::VPMADD52LUQrm,\n    llvm::X86::VPMADDUBSWrm,\n    llvm::X86::VPMADDWDrm,\n    llvm::X86::VPMASKMOVDmr,\n    llvm::X86::VPMASKMOVDrm,\n    llvm::X86::VPMASKMOVQmr,\n    llvm::X86::VPMASKMOVQrm,\n    llvm::X86::VPMAXSBrm,\n    llvm::X86::VPMAXSDrm,\n    llvm::X86::VPMAXSWrm,\n    llvm::X86::VPMAXUBrm,\n    llvm::X86::VPMAXUDrm,\n    llvm::X86::VPMAXUWrm,\n    llvm::X86::VPMINSBrm,\n    llvm::X86::VPMINSDrm,\n    llvm::X86::VPMINSWrm,\n    llvm::X86::VPMINUBrm,\n    llvm::X86::VPMINUDrm,\n    llvm::X86::VPMINUWrm,\n    llvm::X86::VPMOVSXBWYrm,\n    llvm::X86::VPMOVSXDQYrm,\n    llvm::X86::VPMOVSXWDYrm,\n    llvm::X86::VPMOVZXBWYrm,\n    llvm::X86::VPMOVZXDQYrm,\n    llvm::X86::VPMOVZXWDYrm,\n    llvm::X86::VPMULDQrm,\n    llvm::X86::VPMULHRSWrm,\n    llvm::X86::VPMULHUWrm,\n    llvm::X86::VPMULHWrm,\n    llvm::X86::VPMULLDrm,\n    llvm::X86::VPMULLWrm,\n    llvm::X86::VPMULUDQrm,\n    llvm::X86::VPORrm,\n    llvm::X86::VPSADBWrm,\n    llvm::X86::VPSHUFBrm,\n    llvm::X86::VPSHUFDmi,\n    llvm::X86::VPSHUFHWmi,\n    llvm::X86::VPSHUFLWmi,\n    llvm::X86::VPSIGNBrm,\n    llvm::X86::VPSIGNDrm,\n    llvm::X86::VPSIGNWrm,\n    llvm::X86::VPSLLDYrm,\n    llvm::X86::VPSLLDrm,\n    llvm::X86::VPSLLQYrm,\n    llvm::X86::VPSLLQrm,\n    llvm::X86::VPSLLVDrm,\n    llvm::X86::VPSLLVQrm,\n    llvm::X86::VPSLLWYrm,\n    llvm::X86::VPSLLWrm,\n    llvm::X86::VPSRADYrm,\n    llvm::X86::VPSRADrm,\n    llvm::X86::VPSRAVDrm,\n    llvm::X86::VPSRAWYrm,\n    llvm::X86::VPSRAWrm,\n    llvm::X86::VPSRLDYrm,\n    llvm::X86::VPSRLDrm,\n    llvm::X86::VPSRLQYrm,\n    llvm::X86::VPSRLQrm,\n    llvm::X86::VPSRLVDrm,\n    llvm::X86::VPSRLVQrm,\n    llvm::X86::VPSRLWYrm,\n    llvm::X86::VPSRLWrm,\n    llvm::X86::VPSUBBrm,\n    llvm::X86::VPSUBDrm,\n    llvm::X86::VPSUBQrm,\n    llvm::X86::VPSUBSBrm,\n    llvm::X86::VPSUBSWrm,\n    llvm::X86::VPSUBUSBrm,\n    llvm::X86::VPSUBUSWrm,\n    llvm::X86::VPSUBWrm,\n    llvm::X86::VPTESTrm,\n    llvm::X86::VPUNPCKHBWrm,\n    llvm::X86::VPUNPCKHDQrm,\n    llvm::X86::VPUNPCKHQDQrm,\n    llvm::X86::VPUNPCKHWDrm,\n    llvm::X86::VPUNPCKLBWrm,\n    llvm::X86::VPUNPCKLDQrm,\n    llvm::X86::VPUNPCKLQDQrm,\n    llvm::X86::VPUNPCKLWDrm,\n    llvm::X86::VPXORrm,\n    llvm::X86::VRCPPSm,\n    llvm::X86::VROUNDPDmi,\n    llvm::X86::VROUNDPSmi,\n    llvm::X86::VRSQRTPSm,\n    llvm::X86::VSHUFPDrmi,\n    llvm::X86::VSHUFPSrmi,\n    llvm::X86::VSM3MSG1rm,\n    llvm::X86::VSM3MSG2rm,\n    llvm::X86::VSM3RNDS2rm,\n    llvm::X86::VSM4KEY4rm,\n    llvm::X86::VSM4RNDS4rm,\n    llvm::X86::VSQRTPDm,\n    llvm::X86::VSQRTPSm,\n    llvm::X86::VSUBPDrm,\n    llvm::X86::VSUBPSrm,\n    llvm::X86::VTESTPDrm,\n    llvm::X86::VTESTPSrm,\n    llvm::X86::VUNPCKHPDrm,\n    llvm::X86::VUNPCKHPSrm,\n    llvm::X86::VUNPCKLPDrm,\n    llvm::X86::VUNPCKLPSrm,\n    llvm::X86::VXORPDrm,\n    llvm::X86::VXORPSrm,\n    llvm::X86::XORPDrm,\n    llvm::X86::XORPSrm,\n    // clang-format on\n};\n\nconstexpr size_t READ_128_SIZE = sizeof(READ_128) / sizeof(unsigned);\n\nconstexpr unsigned READ_224[] = {\n    // clang-format off\n    llvm::X86::FLDENVm,\n    // clang-format on\n};\n\nconstexpr size_t READ_224_SIZE = sizeof(READ_224) / sizeof(unsigned);\n\nconstexpr unsigned READ_256[] = {\n    // clang-format off\n    llvm::X86::VADDPDYrm,\n    llvm::X86::VADDPSYrm,\n    llvm::X86::VADDSUBPDYrm,\n    llvm::X86::VADDSUBPSYrm,\n    llvm::X86::VAESDECLASTYrm,\n    llvm::X86::VAESDECLASTZ256rm,\n    llvm::X86::VAESDECYrm,\n    llvm::X86::VAESDECZ256rm,\n    llvm::X86::VAESENCLASTYrm,\n    llvm::X86::VAESENCLASTZ256rm,\n    llvm::X86::VAESENCYrm,\n    llvm::X86::VAESENCZ256rm,\n    llvm::X86::VANDNPDYrm,\n    llvm::X86::VANDNPSYrm,\n    llvm::X86::VANDPDYrm,\n    llvm::X86::VANDPSYrm,\n    llvm::X86::VBLENDPDYrmi,\n    llvm::X86::VBLENDPSYrmi,\n    llvm::X86::VBLENDVPDYrmr,\n    llvm::X86::VBLENDVPSYrmr,\n    llvm::X86::VCMPPDYrmi,\n    llvm::X86::VCMPPSYrmi,\n    llvm::X86::VCVTDQ2PSYrm,\n    llvm::X86::VCVTNEEBF162PSYrm,\n    llvm::X86::VCVTNEEPH2PSYrm,\n    llvm::X86::VCVTNEOBF162PSYrm,\n    llvm::X86::VCVTNEOPH2PSYrm,\n    llvm::X86::VCVTNEPS2BF16Yrm,\n    llvm::X86::VCVTPD2DQYrm,\n    llvm::X86::VCVTPD2PSYrm,\n    llvm::X86::VCVTPS2DQYrm,\n    llvm::X86::VCVTTPD2DQYrm,\n    llvm::X86::VCVTTPS2DQYrm,\n    llvm::X86::VDIVPDYrm,\n    llvm::X86::VDIVPSYrm,\n    llvm::X86::VDPPSYrmi,\n    llvm::X86::VFMADD132PDYm,\n    llvm::X86::VFMADD132PSYm,\n    llvm::X86::VFMADD213PDYm,\n    llvm::X86::VFMADD213PSYm,\n    llvm::X86::VFMADD231PDYm,\n    llvm::X86::VFMADD231PSYm,\n    llvm::X86::VFMADDPD4Ymr,\n    llvm::X86::VFMADDPD4Yrm,\n    llvm::X86::VFMADDPS4Ymr,\n    llvm::X86::VFMADDPS4Yrm,\n    llvm::X86::VFMADDSUB132PDYm,\n    llvm::X86::VFMADDSUB132PSYm,\n    llvm::X86::VFMADDSUB213PDYm,\n    llvm::X86::VFMADDSUB213PSYm,\n    llvm::X86::VFMADDSUB231PDYm,\n    llvm::X86::VFMADDSUB231PSYm,\n    llvm::X86::VFMADDSUBPD4Ymr,\n    llvm::X86::VFMADDSUBPD4Yrm,\n    llvm::X86::VFMADDSUBPS4Ymr,\n    llvm::X86::VFMADDSUBPS4Yrm,\n    llvm::X86::VFMSUB132PDYm,\n    llvm::X86::VFMSUB132PSYm,\n    llvm::X86::VFMSUB213PDYm,\n    llvm::X86::VFMSUB213PSYm,\n    llvm::X86::VFMSUB231PDYm,\n    llvm::X86::VFMSUB231PSYm,\n    llvm::X86::VFMSUBADD132PDYm,\n    llvm::X86::VFMSUBADD132PSYm,\n    llvm::X86::VFMSUBADD213PDYm,\n    llvm::X86::VFMSUBADD213PSYm,\n    llvm::X86::VFMSUBADD231PDYm,\n    llvm::X86::VFMSUBADD231PSYm,\n    llvm::X86::VFMSUBADDPD4Ymr,\n    llvm::X86::VFMSUBADDPD4Yrm,\n    llvm::X86::VFMSUBADDPS4Ymr,\n    llvm::X86::VFMSUBADDPS4Yrm,\n    llvm::X86::VFMSUBPD4Ymr,\n    llvm::X86::VFMSUBPD4Yrm,\n    llvm::X86::VFMSUBPS4Ymr,\n    llvm::X86::VFMSUBPS4Yrm,\n    llvm::X86::VFNMADD132PDYm,\n    llvm::X86::VFNMADD132PSYm,\n    llvm::X86::VFNMADD213PDYm,\n    llvm::X86::VFNMADD213PSYm,\n    llvm::X86::VFNMADD231PDYm,\n    llvm::X86::VFNMADD231PSYm,\n    llvm::X86::VFNMADDPD4Ymr,\n    llvm::X86::VFNMADDPD4Yrm,\n    llvm::X86::VFNMADDPS4Ymr,\n    llvm::X86::VFNMADDPS4Yrm,\n    llvm::X86::VFNMSUB132PDYm,\n    llvm::X86::VFNMSUB132PSYm,\n    llvm::X86::VFNMSUB213PDYm,\n    llvm::X86::VFNMSUB213PSYm,\n    llvm::X86::VFNMSUB231PDYm,\n    llvm::X86::VFNMSUB231PSYm,\n    llvm::X86::VFNMSUBPD4Ymr,\n    llvm::X86::VFNMSUBPD4Yrm,\n    llvm::X86::VFNMSUBPS4Ymr,\n    llvm::X86::VFNMSUBPS4Yrm,\n    llvm::X86::VGF2P8AFFINEINVQBYrmi,\n    llvm::X86::VGF2P8AFFINEQBYrmi,\n    llvm::X86::VGF2P8MULBYrm,\n    llvm::X86::VHADDPDYrm,\n    llvm::X86::VHADDPSYrm,\n    llvm::X86::VHSUBPDYrm,\n    llvm::X86::VHSUBPSYrm,\n    llvm::X86::VLDDQUYrm,\n    llvm::X86::VMASKMOVPDYmr,\n    llvm::X86::VMASKMOVPDYrm,\n    llvm::X86::VMASKMOVPSYmr,\n    llvm::X86::VMASKMOVPSYrm,\n    llvm::X86::VMAXPDYrm,\n    llvm::X86::VMAXPSYrm,\n    llvm::X86::VMINPDYrm,\n    llvm::X86::VMINPSYrm,\n    llvm::X86::VMOVAPDYrm,\n    llvm::X86::VMOVAPSYrm,\n    llvm::X86::VMOVDDUPYrm,\n    llvm::X86::VMOVDQAYrm,\n    llvm::X86::VMOVDQUYrm,\n    llvm::X86::VMOVNTDQAYrm,\n    llvm::X86::VMOVSHDUPYrm,\n    llvm::X86::VMOVSLDUPYrm,\n    llvm::X86::VMOVUPDYrm,\n    llvm::X86::VMOVUPSYrm,\n    llvm::X86::VMPSADBWYrmi,\n    llvm::X86::VMULPDYrm,\n    llvm::X86::VMULPSYrm,\n    llvm::X86::VORPDYrm,\n    llvm::X86::VORPSYrm,\n    llvm::X86::VP2INTERSECTDZ256rm,\n    llvm::X86::VP2INTERSECTQZ256rm,\n    llvm::X86::VPABSBYrm,\n    llvm::X86::VPABSDYrm,\n    llvm::X86::VPABSWYrm,\n    llvm::X86::VPACKSSDWYrm,\n    llvm::X86::VPACKSSWBYrm,\n    llvm::X86::VPACKUSDWYrm,\n    llvm::X86::VPACKUSWBYrm,\n    llvm::X86::VPADDBYrm,\n    llvm::X86::VPADDDYrm,\n    llvm::X86::VPADDQYrm,\n    llvm::X86::VPADDSBYrm,\n    llvm::X86::VPADDSWYrm,\n    llvm::X86::VPADDUSBYrm,\n    llvm::X86::VPADDUSWYrm,\n    llvm::X86::VPADDWYrm,\n    llvm::X86::VPALIGNRYrmi,\n    llvm::X86::VPANDNYrm,\n    llvm::X86::VPANDYrm,\n    llvm::X86::VPAVGBYrm,\n    llvm::X86::VPAVGWYrm,\n    llvm::X86::VPBLENDDYrmi,\n    llvm::X86::VPBLENDVBYrmr,\n    llvm::X86::VPBLENDWYrmi,\n    llvm::X86::VPCLMULQDQYrmi,\n    llvm::X86::VPCLMULQDQZ256rmi,\n    llvm::X86::VPCMPEQBYrm,\n    llvm::X86::VPCMPEQDYrm,\n    llvm::X86::VPCMPEQQYrm,\n    llvm::X86::VPCMPEQWYrm,\n    llvm::X86::VPCMPGTBYrm,\n    llvm::X86::VPCMPGTDYrm,\n    llvm::X86::VPCMPGTQYrm,\n    llvm::X86::VPCMPGTWYrm,\n    llvm::X86::VPDPBSSDSYrm,\n    llvm::X86::VPDPBSSDYrm,\n    llvm::X86::VPDPBSUDSYrm,\n    llvm::X86::VPDPBSUDYrm,\n    llvm::X86::VPDPBUSDSYrm,\n    llvm::X86::VPDPBUSDYrm,\n    llvm::X86::VPDPBUUDSYrm,\n    llvm::X86::VPDPBUUDYrm,\n    llvm::X86::VPDPWSSDSYrm,\n    llvm::X86::VPDPWSSDYrm,\n    llvm::X86::VPDPWSUDSYrm,\n    llvm::X86::VPDPWSUDYrm,\n    llvm::X86::VPDPWUSDSYrm,\n    llvm::X86::VPDPWUSDYrm,\n    llvm::X86::VPDPWUUDSYrm,\n    llvm::X86::VPDPWUUDYrm,\n    llvm::X86::VPERM2F128rm,\n    llvm::X86::VPERM2I128rm,\n    llvm::X86::VPERMDYrm,\n    llvm::X86::VPERMIL2PDYmr,\n    llvm::X86::VPERMIL2PDYrm,\n    llvm::X86::VPERMIL2PSYmr,\n    llvm::X86::VPERMIL2PSYrm,\n    llvm::X86::VPERMILPDYmi,\n    llvm::X86::VPERMILPDYrm,\n    llvm::X86::VPERMILPSYmi,\n    llvm::X86::VPERMILPSYrm,\n    llvm::X86::VPERMPDYmi,\n    llvm::X86::VPERMPSYrm,\n    llvm::X86::VPERMQYmi,\n    llvm::X86::VPHADDDYrm,\n    llvm::X86::VPHADDSWYrm,\n    llvm::X86::VPHADDWYrm,\n    llvm::X86::VPHSUBDYrm,\n    llvm::X86::VPHSUBSWYrm,\n    llvm::X86::VPHSUBWYrm,\n    llvm::X86::VPMADD52HUQYrm,\n    llvm::X86::VPMADD52LUQYrm,\n    llvm::X86::VPMADDUBSWYrm,\n    llvm::X86::VPMADDWDYrm,\n    llvm::X86::VPMASKMOVDYmr,\n    llvm::X86::VPMASKMOVDYrm,\n    llvm::X86::VPMASKMOVQYmr,\n    llvm::X86::VPMASKMOVQYrm,\n    llvm::X86::VPMAXSBYrm,\n    llvm::X86::VPMAXSDYrm,\n    llvm::X86::VPMAXSWYrm,\n    llvm::X86::VPMAXUBYrm,\n    llvm::X86::VPMAXUDYrm,\n    llvm::X86::VPMAXUWYrm,\n    llvm::X86::VPMINSBYrm,\n    llvm::X86::VPMINSDYrm,\n    llvm::X86::VPMINSWYrm,\n    llvm::X86::VPMINUBYrm,\n    llvm::X86::VPMINUDYrm,\n    llvm::X86::VPMINUWYrm,\n    llvm::X86::VPMULDQYrm,\n    llvm::X86::VPMULHRSWYrm,\n    llvm::X86::VPMULHUWYrm,\n    llvm::X86::VPMULHWYrm,\n    llvm::X86::VPMULLDYrm,\n    llvm::X86::VPMULLWYrm,\n    llvm::X86::VPMULUDQYrm,\n    llvm::X86::VPORYrm,\n    llvm::X86::VPSADBWYrm,\n    llvm::X86::VPSHUFBYrm,\n    llvm::X86::VPSHUFDYmi,\n    llvm::X86::VPSHUFHWYmi,\n    llvm::X86::VPSHUFLWYmi,\n    llvm::X86::VPSIGNBYrm,\n    llvm::X86::VPSIGNDYrm,\n    llvm::X86::VPSIGNWYrm,\n    llvm::X86::VPSLLVDYrm,\n    llvm::X86::VPSLLVQYrm,\n    llvm::X86::VPSRAVDYrm,\n    llvm::X86::VPSRLVDYrm,\n    llvm::X86::VPSRLVQYrm,\n    llvm::X86::VPSUBBYrm,\n    llvm::X86::VPSUBDYrm,\n    llvm::X86::VPSUBQYrm,\n    llvm::X86::VPSUBSBYrm,\n    llvm::X86::VPSUBSWYrm,\n    llvm::X86::VPSUBUSBYrm,\n    llvm::X86::VPSUBUSWYrm,\n    llvm::X86::VPSUBWYrm,\n    llvm::X86::VPTESTYrm,\n    llvm::X86::VPUNPCKHBWYrm,\n    llvm::X86::VPUNPCKHDQYrm,\n    llvm::X86::VPUNPCKHQDQYrm,\n    llvm::X86::VPUNPCKHWDYrm,\n    llvm::X86::VPUNPCKLBWYrm,\n    llvm::X86::VPUNPCKLDQYrm,\n    llvm::X86::VPUNPCKLQDQYrm,\n    llvm::X86::VPUNPCKLWDYrm,\n    llvm::X86::VPXORYrm,\n    llvm::X86::VRCPPSYm,\n    llvm::X86::VROUNDPDYmi,\n    llvm::X86::VROUNDPSYmi,\n    llvm::X86::VRSQRTPSYm,\n    llvm::X86::VSHUFPDYrmi,\n    llvm::X86::VSHUFPSYrmi,\n    llvm::X86::VSM4KEY4Yrm,\n    llvm::X86::VSM4RNDS4Yrm,\n    llvm::X86::VSQRTPDYm,\n    llvm::X86::VSQRTPSYm,\n    llvm::X86::VSUBPDYrm,\n    llvm::X86::VSUBPSYrm,\n    llvm::X86::VTESTPDYrm,\n    llvm::X86::VTESTPSYrm,\n    llvm::X86::VUNPCKHPDYrm,\n    llvm::X86::VUNPCKHPSYrm,\n    llvm::X86::VUNPCKLPDYrm,\n    llvm::X86::VUNPCKLPSYrm,\n    llvm::X86::VXORPDYrm,\n    llvm::X86::VXORPSYrm,\n    // clang-format on\n};\n\nconstexpr size_t READ_256_SIZE = sizeof(READ_256) / sizeof(unsigned);\n\nconstexpr unsigned READ_384[] = {\n    // clang-format off\n    llvm::X86::AESDEC128KL,\n    llvm::X86::AESDECWIDE128KL,\n    llvm::X86::AESENC128KL,\n    llvm::X86::AESENCWIDE128KL,\n    // clang-format on\n};\n\nconstexpr size_t READ_384_SIZE = sizeof(READ_384) / sizeof(unsigned);\n\nconstexpr unsigned READ_512[] = {\n    // clang-format off\n    llvm::X86::AESDEC256KL,\n    llvm::X86::AESDECWIDE256KL,\n    llvm::X86::AESENC256KL,\n    llvm::X86::AESENCWIDE256KL,\n    llvm::X86::LDTILECFG,\n    llvm::X86::LDTILECFG_EVEX,\n    llvm::X86::VAESDECLASTZrm,\n    llvm::X86::VAESDECZrm,\n    llvm::X86::VAESENCLASTZrm,\n    llvm::X86::VAESENCZrm,\n    llvm::X86::VP2INTERSECTDZrm,\n    llvm::X86::VP2INTERSECTQZrm,\n    llvm::X86::VPCLMULQDQZrmi,\n    // clang-format on\n};\n\nconstexpr size_t READ_512_SIZE = sizeof(READ_512) / sizeof(unsigned);\n\nconstexpr unsigned READ_864[] = {\n    // clang-format off\n    llvm::X86::FRSTORm,\n    // clang-format on\n};\n\nconstexpr size_t READ_864_SIZE = sizeof(READ_864) / sizeof(unsigned);\n\nconstexpr unsigned READ_4096[] = {\n    // clang-format off\n    llvm::X86::FXRSTOR,\n    llvm::X86::FXRSTOR64,\n    llvm::X86::MOVDIR64B16,\n    llvm::X86::MOVDIR64B32,\n    llvm::X86::MOVDIR64B32_EVEX,\n    llvm::X86::MOVDIR64B64,\n    llvm::X86::MOVDIR64B64_EVEX,\n    // clang-format on\n};\n\nconstexpr size_t READ_4096_SIZE = sizeof(READ_4096) / sizeof(unsigned);\n\nconstexpr unsigned READ_4608[] = {\n    // clang-format off\n    llvm::X86::XRSTOR,\n    llvm::X86::XRSTOR64,\n    llvm::X86::XRSTORS,\n    llvm::X86::XRSTORS64,\n    llvm::X86::XSAVE,\n    llvm::X86::XSAVE64,\n    llvm::X86::XSAVEC,\n    llvm::X86::XSAVEC64,\n    llvm::X86::XSAVEOPT,\n    llvm::X86::XSAVEOPT64,\n    llvm::X86::XSAVES,\n    llvm::X86::XSAVES64,\n    // clang-format on\n};\n\nconstexpr size_t READ_4608_SIZE = sizeof(READ_4608) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_8[] = {\n    // clang-format off\n    llvm::X86::ADC8mi,\n    llvm::X86::ADC8mi_EVEX,\n    llvm::X86::ADC8mi8,\n    llvm::X86::ADC8mr,\n    llvm::X86::ADC8mr_EVEX,\n    llvm::X86::ADD8mi,\n    llvm::X86::ADD8mi8,\n    llvm::X86::ADD8mi_EVEX,\n    llvm::X86::ADD8mi_NF,\n    llvm::X86::ADD8mr,\n    llvm::X86::ADD8mr_EVEX,\n    llvm::X86::ADD8mr_NF,\n    llvm::X86::AND8mi,\n    llvm::X86::AND8mi8,\n    llvm::X86::AND8mi_EVEX,\n    llvm::X86::AND8mi_NF,\n    llvm::X86::AND8mr,\n    llvm::X86::AND8mr_EVEX,\n    llvm::X86::AND8mr_NF,\n    llvm::X86::CMPXCHG8rm,\n    llvm::X86::DEC8m,\n    llvm::X86::DEC8m_EVEX,\n    llvm::X86::DEC8m_NF,\n    llvm::X86::INC8m,\n    llvm::X86::INC8m_EVEX,\n    llvm::X86::INC8m_NF,\n    llvm::X86::KMOVBmk,\n    llvm::X86::KMOVBmk_EVEX,\n    llvm::X86::LCMPXCHG8,\n    llvm::X86::LOCK_ADD8mi,\n    llvm::X86::LOCK_ADD8mr,\n    llvm::X86::LOCK_AND8mi,\n    llvm::X86::LOCK_AND8mr,\n    llvm::X86::LOCK_DEC8m,\n    llvm::X86::LOCK_INC8m,\n    llvm::X86::LOCK_OR8mi,\n    llvm::X86::LOCK_OR8mr,\n    llvm::X86::LOCK_SUB8mi,\n    llvm::X86::LOCK_SUB8mr,\n    llvm::X86::LOCK_XOR8mi,\n    llvm::X86::LOCK_XOR8mr,\n    llvm::X86::MOV8mi,\n    llvm::X86::MOV8mr,\n    llvm::X86::MOV8mr_NOREX,\n    llvm::X86::MOV8o16a,\n    llvm::X86::MOV8o32a,\n    llvm::X86::MOV8o64a,\n    llvm::X86::MOVSB,\n    llvm::X86::NEG8m,\n    llvm::X86::NEG8m_EVEX,\n    llvm::X86::NEG8m_NF,\n    llvm::X86::NOT8m,\n    llvm::X86::NOT8m_EVEX,\n    llvm::X86::OR8mi,\n    llvm::X86::OR8mi8,\n    llvm::X86::OR8mi_EVEX,\n    llvm::X86::OR8mi_NF,\n    llvm::X86::OR8mr,\n    llvm::X86::OR8mr_EVEX,\n    llvm::X86::OR8mr_NF,\n    llvm::X86::PEXTRBmr,\n    llvm::X86::RCL8m1,\n    llvm::X86::RCL8m1_EVEX,\n    llvm::X86::RCL8mCL,\n    llvm::X86::RCL8mCL_EVEX,\n    llvm::X86::RCL8mi,\n    llvm::X86::RCL8mi_EVEX,\n    llvm::X86::RCR8m1,\n    llvm::X86::RCR8m1_EVEX,\n    llvm::X86::RCR8mCL,\n    llvm::X86::RCR8mCL_EVEX,\n    llvm::X86::RCR8mi,\n    llvm::X86::RCR8mi_EVEX,\n    llvm::X86::ROL8m1,\n    llvm::X86::ROL8m1_EVEX,\n    llvm::X86::ROL8m1_NF,\n    llvm::X86::ROL8mCL,\n    llvm::X86::ROL8mCL_EVEX,\n    llvm::X86::ROL8mCL_NF,\n    llvm::X86::ROL8mi,\n    llvm::X86::ROL8mi_EVEX,\n    llvm::X86::ROL8mi_NF,\n    llvm::X86::ROR8m1,\n    llvm::X86::ROR8m1_EVEX,\n    llvm::X86::ROR8m1_NF,\n    llvm::X86::ROR8mCL,\n    llvm::X86::ROR8mCL_EVEX,\n    llvm::X86::ROR8mCL_NF,\n    llvm::X86::ROR8mi,\n    llvm::X86::ROR8mi_EVEX,\n    llvm::X86::ROR8mi_NF,\n    llvm::X86::SAR8m1,\n    llvm::X86::SAR8m1_EVEX,\n    llvm::X86::SAR8m1_NF,\n    llvm::X86::SAR8mCL,\n    llvm::X86::SAR8mCL_EVEX,\n    llvm::X86::SAR8mCL_NF,\n    llvm::X86::SAR8mi,\n    llvm::X86::SAR8mi_EVEX,\n    llvm::X86::SAR8mi_NF,\n    llvm::X86::SBB8mi,\n    llvm::X86::SBB8mi_EVEX,\n    llvm::X86::SBB8mr,\n    llvm::X86::SBB8mr_EVEX,\n    llvm::X86::SETCCm,\n    llvm::X86::SETCCm_EVEX,\n    llvm::X86::SETZUCCm,\n    llvm::X86::SHL8m1,\n    llvm::X86::SHL8m1_EVEX,\n    llvm::X86::SHL8m1_NF,\n    llvm::X86::SHL8mCL,\n    llvm::X86::SHL8mCL_EVEX,\n    llvm::X86::SHL8mCL_NF,\n    llvm::X86::SHL8mi,\n    llvm::X86::SHL8mi_EVEX,\n    llvm::X86::SHL8mi_NF,\n    llvm::X86::SHR8m1,\n    llvm::X86::SHR8m1_EVEX,\n    llvm::X86::SHR8m1_NF,\n    llvm::X86::SHR8mCL,\n    llvm::X86::SHR8mCL_EVEX,\n    llvm::X86::SHR8mCL_NF,\n    llvm::X86::SHR8mi,\n    llvm::X86::SHR8mi_EVEX,\n    llvm::X86::SHR8mi_NF,\n    llvm::X86::STOSB,\n    llvm::X86::SUB8mi,\n    llvm::X86::SUB8mi_EVEX,\n    llvm::X86::SUB8mi_NF,\n    llvm::X86::SUB8mi8,\n    llvm::X86::SUB8mr,\n    llvm::X86::SUB8mr_EVEX,\n    llvm::X86::SUB8mr_NF,\n    llvm::X86::VPEXTRBmr,\n    llvm::X86::XADD8rm,\n    llvm::X86::XCHG8rm,\n    llvm::X86::XOR8mi,\n    llvm::X86::XOR8mi_EVEX,\n    llvm::X86::XOR8mi_NF,\n    llvm::X86::XOR8mi8,\n    llvm::X86::XOR8mr,\n    llvm::X86::XOR8mr_EVEX,\n    llvm::X86::XOR8mr_NF,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_8_SIZE = sizeof(WRITE_8) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_16[] = {\n    // clang-format off\n    llvm::X86::ADC16mi,\n    llvm::X86::ADC16mi8,\n    llvm::X86::ADC16mi8_EVEX,\n    llvm::X86::ADC16mi_EVEX,\n    llvm::X86::ADC16mr,\n    llvm::X86::ADC16mr_EVEX,\n    llvm::X86::ADD16mi,\n    llvm::X86::ADD16mi8,\n    llvm::X86::ADD16mi8_EVEX,\n    llvm::X86::ADD16mi8_NF,\n    llvm::X86::ADD16mi_EVEX,\n    llvm::X86::ADD16mi_NF,\n    llvm::X86::ADD16mr,\n    llvm::X86::ADD16mr_EVEX,\n    llvm::X86::ADD16mr_NF,\n    llvm::X86::AND16mi,\n    llvm::X86::AND16mi8,\n    llvm::X86::AND16mi8_EVEX,\n    llvm::X86::AND16mi8_NF,\n    llvm::X86::AND16mi_EVEX,\n    llvm::X86::AND16mi_NF,\n    llvm::X86::AND16mr,\n    llvm::X86::AND16mr_EVEX,\n    llvm::X86::AND16mr_NF,\n    llvm::X86::ARPL16mr,\n    llvm::X86::BTC16mi8,\n    llvm::X86::BTC16mr,\n    llvm::X86::BTR16mi8,\n    llvm::X86::BTR16mr,\n    llvm::X86::BTS16mi8,\n    llvm::X86::BTS16mr,\n    llvm::X86::CFCMOV16mr,\n    llvm::X86::CMPXCHG16rm,\n    llvm::X86::DEC16m,\n    llvm::X86::DEC16m_EVEX,\n    llvm::X86::DEC16m_NF,\n    llvm::X86::FNSTCW16m,\n    llvm::X86::FNSTSWm,\n    llvm::X86::INC16m,\n    llvm::X86::INC16m_EVEX,\n    llvm::X86::INC16m_NF,\n    llvm::X86::ISTT_FP16m,\n    llvm::X86::IST_F16m,\n    llvm::X86::IST_FP16m,\n    llvm::X86::KMOVWmk,\n    llvm::X86::KMOVWmk_EVEX,\n    llvm::X86::LCMPXCHG16,\n    llvm::X86::LOCK_ADD16mi,\n    llvm::X86::LOCK_ADD16mi8,\n    llvm::X86::LOCK_ADD16mr,\n    llvm::X86::LOCK_AND16mi,\n    llvm::X86::LOCK_AND16mi8,\n    llvm::X86::LOCK_AND16mr,\n    llvm::X86::LOCK_BTC16m,\n    llvm::X86::LOCK_BTR16m,\n    llvm::X86::LOCK_BTS16m,\n    llvm::X86::LOCK_DEC16m,\n    llvm::X86::LOCK_INC16m,\n    llvm::X86::LOCK_OR16mi,\n    llvm::X86::LOCK_OR16mi8,\n    llvm::X86::LOCK_OR16mr,\n    llvm::X86::LOCK_SUB16mi,\n    llvm::X86::LOCK_SUB16mi8,\n    llvm::X86::LOCK_SUB16mr,\n    llvm::X86::LOCK_XOR16mi,\n    llvm::X86::LOCK_XOR16mi8,\n    llvm::X86::LOCK_XOR16mr,\n    llvm::X86::MOV16mi,\n    llvm::X86::MOV16mr,\n    llvm::X86::MOV16ms,\n    llvm::X86::MOV16o16a,\n    llvm::X86::MOV16o32a,\n    llvm::X86::MOV16o64a,\n    llvm::X86::MOVBE16mr,\n    llvm::X86::MOVBE16mr_EVEX,\n    llvm::X86::MOVSW,\n    llvm::X86::NEG16m,\n    llvm::X86::NEG16m_EVEX,\n    llvm::X86::NEG16m_NF,\n    llvm::X86::NOT16m,\n    llvm::X86::NOT16m_EVEX,\n    llvm::X86::OR16mi,\n    llvm::X86::OR16mi8,\n    llvm::X86::OR16mi8_EVEX,\n    llvm::X86::OR16mi8_NF,\n    llvm::X86::OR16mi_EVEX,\n    llvm::X86::OR16mi_NF,\n    llvm::X86::OR16mr,\n    llvm::X86::OR16mr_EVEX,\n    llvm::X86::OR16mr_NF,\n    llvm::X86::PEXTRWmr,\n    llvm::X86::POP16rmm,\n    llvm::X86::RCL16m1,\n    llvm::X86::RCL16m1_EVEX,\n    llvm::X86::RCL16mCL,\n    llvm::X86::RCL16mCL_EVEX,\n    llvm::X86::RCL16mi,\n    llvm::X86::RCL16mi_EVEX,\n    llvm::X86::RCR16m1,\n    llvm::X86::RCR16m1_EVEX,\n    llvm::X86::RCR16mCL,\n    llvm::X86::RCR16mCL_EVEX,\n    llvm::X86::RCR16mi,\n    llvm::X86::RCR16mi_EVEX,\n    llvm::X86::ROL16m1,\n    llvm::X86::ROL16m1_EVEX,\n    llvm::X86::ROL16m1_NF,\n    llvm::X86::ROL16mCL,\n    llvm::X86::ROL16mCL_EVEX,\n    llvm::X86::ROL16mCL_NF,\n    llvm::X86::ROL16mi,\n    llvm::X86::ROL16mi_EVEX,\n    llvm::X86::ROL16mi_NF,\n    llvm::X86::ROR16m1,\n    llvm::X86::ROR16m1_EVEX,\n    llvm::X86::ROR16m1_NF,\n    llvm::X86::ROR16mCL,\n    llvm::X86::ROR16mCL_EVEX,\n    llvm::X86::ROR16mCL_NF,\n    llvm::X86::ROR16mi,\n    llvm::X86::ROR16mi_EVEX,\n    llvm::X86::ROR16mi_NF,\n    llvm::X86::SAR16m1,\n    llvm::X86::SAR16m1_EVEX,\n    llvm::X86::SAR16m1_NF,\n    llvm::X86::SAR16mCL,\n    llvm::X86::SAR16mCL_EVEX,\n    llvm::X86::SAR16mCL_NF,\n    llvm::X86::SAR16mi,\n    llvm::X86::SAR16mi_EVEX,\n    llvm::X86::SAR16mi_NF,\n    llvm::X86::SBB16mi,\n    llvm::X86::SBB16mi8,\n    llvm::X86::SBB16mi8_EVEX,\n    llvm::X86::SBB16mi_EVEX,\n    llvm::X86::SBB16mr,\n    llvm::X86::SBB16mr_EVEX,\n    llvm::X86::SHL16m1,\n    llvm::X86::SHL16m1_EVEX,\n    llvm::X86::SHL16m1_NF,\n    llvm::X86::SHL16mCL,\n    llvm::X86::SHL16mCL_EVEX,\n    llvm::X86::SHL16mCL_NF,\n    llvm::X86::SHL16mi,\n    llvm::X86::SHL16mi_EVEX,\n    llvm::X86::SHL16mi_NF,\n    llvm::X86::SHLD16mrCL,\n    llvm::X86::SHLD16mrCL_EVEX,\n    llvm::X86::SHLD16mrCL_NF,\n    llvm::X86::SHLD16mri8,\n    llvm::X86::SHLD16mri8_EVEX,\n    llvm::X86::SHLD16mri8_NF,\n    llvm::X86::SHR16m1,\n    llvm::X86::SHR16m1_EVEX,\n    llvm::X86::SHR16m1_NF,\n    llvm::X86::SHR16mCL,\n    llvm::X86::SHR16mCL_EVEX,\n    llvm::X86::SHR16mCL_NF,\n    llvm::X86::SHR16mi,\n    llvm::X86::SHR16mi_EVEX,\n    llvm::X86::SHR16mi_NF,\n    llvm::X86::SHRD16mrCL,\n    llvm::X86::SHRD16mrCL_EVEX,\n    llvm::X86::SHRD16mrCL_NF,\n    llvm::X86::SHRD16mri8,\n    llvm::X86::SHRD16mri8_EVEX,\n    llvm::X86::SHRD16mri8_NF,\n    llvm::X86::SLDT16m,\n    llvm::X86::STOSW,\n    llvm::X86::STRm,\n    llvm::X86::SUB16mi,\n    llvm::X86::SUB16mi8,\n    llvm::X86::SUB16mi8_EVEX,\n    llvm::X86::SUB16mi8_NF,\n    llvm::X86::SUB16mi_EVEX,\n    llvm::X86::SUB16mi_NF,\n    llvm::X86::SUB16mr,\n    llvm::X86::SUB16mr_EVEX,\n    llvm::X86::SUB16mr_NF,\n    llvm::X86::VPEXTRWmr,\n    llvm::X86::XADD16rm,\n    llvm::X86::XCHG16rm,\n    llvm::X86::XOR16mi,\n    llvm::X86::XOR16mi_EVEX,\n    llvm::X86::XOR16mi_NF,\n    llvm::X86::XOR16mi8,\n    llvm::X86::XOR16mi8_EVEX,\n    llvm::X86::XOR16mi8_NF,\n    llvm::X86::XOR16mr,\n    llvm::X86::XOR16mr_EVEX,\n    llvm::X86::XOR16mr_NF,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_16_SIZE = sizeof(WRITE_16) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_32[] = {\n    // clang-format off\n    llvm::X86::AADD32mr,\n    llvm::X86::AADD32mr_EVEX,\n    llvm::X86::AAND32mr,\n    llvm::X86::AAND32mr_EVEX,\n    llvm::X86::ADC32mi,\n    llvm::X86::ADC32mi8,\n    llvm::X86::ADC32mi8_EVEX,\n    llvm::X86::ADC32mi_EVEX,\n    llvm::X86::ADC32mr,\n    llvm::X86::ADC32mr_EVEX,\n    llvm::X86::ADD32mi,\n    llvm::X86::ADD32mi8,\n    llvm::X86::ADD32mi8_EVEX,\n    llvm::X86::ADD32mi8_NF,\n    llvm::X86::ADD32mi_EVEX,\n    llvm::X86::ADD32mi_NF,\n    llvm::X86::ADD32mr,\n    llvm::X86::ADD32mr_EVEX,\n    llvm::X86::ADD32mr_NF,\n    llvm::X86::AND32mi,\n    llvm::X86::AND32mi8,\n    llvm::X86::AND32mi8_EVEX,\n    llvm::X86::AND32mi8_NF,\n    llvm::X86::AND32mi_EVEX,\n    llvm::X86::AND32mi_NF,\n    llvm::X86::AND32mr,\n    llvm::X86::AND32mr_EVEX,\n    llvm::X86::AND32mr_NF,\n    llvm::X86::AOR32mr,\n    llvm::X86::AOR32mr_EVEX,\n    llvm::X86::AXOR32mr,\n    llvm::X86::AXOR32mr_EVEX,\n    llvm::X86::BTC32mi8,\n    llvm::X86::BTC32mr,\n    llvm::X86::BTR32mi8,\n    llvm::X86::BTR32mr,\n    llvm::X86::BTS32mi8,\n    llvm::X86::BTS32mr,\n    llvm::X86::CFCMOV32mr,\n    llvm::X86::CMPCCXADDmr32,\n    llvm::X86::CMPCCXADDmr32_EVEX,\n    llvm::X86::CMPXCHG32rm,\n    llvm::X86::DEC32m,\n    llvm::X86::DEC32m_EVEX,\n    llvm::X86::DEC32m_NF,\n    llvm::X86::EXTRACTPSmr,\n    llvm::X86::INC32m,\n    llvm::X86::INC32m_EVEX,\n    llvm::X86::INC32m_NF,\n    llvm::X86::ISTT_FP32m,\n    llvm::X86::IST_F32m,\n    llvm::X86::IST_FP32m,\n    llvm::X86::KMOVDmk,\n    llvm::X86::KMOVDmk_EVEX,\n    llvm::X86::LCMPXCHG32,\n    llvm::X86::LOCK_ADD32mi,\n    llvm::X86::LOCK_ADD32mi8,\n    llvm::X86::LOCK_ADD32mr,\n    llvm::X86::LOCK_AND32mi,\n    llvm::X86::LOCK_AND32mi8,\n    llvm::X86::LOCK_AND32mr,\n    llvm::X86::LOCK_BTC32m,\n    llvm::X86::LOCK_BTR32m,\n    llvm::X86::LOCK_BTS32m,\n    llvm::X86::LOCK_DEC32m,\n    llvm::X86::LOCK_INC32m,\n    llvm::X86::LOCK_OR32mi,\n    llvm::X86::LOCK_OR32mi8,\n    llvm::X86::LOCK_OR32mr,\n    llvm::X86::LOCK_SUB32mi,\n    llvm::X86::LOCK_SUB32mi8,\n    llvm::X86::LOCK_SUB32mr,\n    llvm::X86::LOCK_XOR32mi,\n    llvm::X86::LOCK_XOR32mi8,\n    llvm::X86::LOCK_XOR32mr,\n    llvm::X86::MMX_MOVD64mr,\n    llvm::X86::MOV32mi,\n    llvm::X86::MOV32mr,\n    llvm::X86::MOV32o16a,\n    llvm::X86::MOV32o32a,\n    llvm::X86::MOV32o64a,\n    llvm::X86::MOVBE32mr,\n    llvm::X86::MOVBE32mr_EVEX,\n    llvm::X86::MOVDIRI32,\n    llvm::X86::MOVDIRI32_EVEX,\n    llvm::X86::MOVNTImr,\n    llvm::X86::MOVNTSS,\n    llvm::X86::MOVPDI2DImr,\n    llvm::X86::MOVSL,\n    llvm::X86::MOVSSmr,\n    llvm::X86::NEG32m,\n    llvm::X86::NEG32m_EVEX,\n    llvm::X86::NEG32m_NF,\n    llvm::X86::NOT32m,\n    llvm::X86::NOT32m_EVEX,\n    llvm::X86::OR32mi,\n    llvm::X86::OR32mi8,\n    llvm::X86::OR32mi8Locked,\n    llvm::X86::OR32mi8_EVEX,\n    llvm::X86::OR32mi8_NF,\n    llvm::X86::OR32mi_EVEX,\n    llvm::X86::OR32mi_NF,\n    llvm::X86::OR32mr,\n    llvm::X86::OR32mr_EVEX,\n    llvm::X86::OR32mr_NF,\n    llvm::X86::PEXTRDmr,\n    llvm::X86::POP32rmm,\n    llvm::X86::RCL32m1,\n    llvm::X86::RCL32m1_EVEX,\n    llvm::X86::RCL32mCL,\n    llvm::X86::RCL32mCL_EVEX,\n    llvm::X86::RCL32mi,\n    llvm::X86::RCL32mi_EVEX,\n    llvm::X86::RCR32m1,\n    llvm::X86::RCR32m1_EVEX,\n    llvm::X86::RCR32mCL,\n    llvm::X86::RCR32mCL_EVEX,\n    llvm::X86::RCR32mi,\n    llvm::X86::RCR32mi_EVEX,\n    llvm::X86::ROL32m1,\n    llvm::X86::ROL32m1_EVEX,\n    llvm::X86::ROL32m1_NF,\n    llvm::X86::ROL32mCL,\n    llvm::X86::ROL32mCL_EVEX,\n    llvm::X86::ROL32mCL_NF,\n    llvm::X86::ROL32mi,\n    llvm::X86::ROL32mi_EVEX,\n    llvm::X86::ROL32mi_NF,\n    llvm::X86::ROR32m1,\n    llvm::X86::ROR32m1_EVEX,\n    llvm::X86::ROR32m1_NF,\n    llvm::X86::ROR32mCL,\n    llvm::X86::ROR32mCL_EVEX,\n    llvm::X86::ROR32mCL_NF,\n    llvm::X86::ROR32mi,\n    llvm::X86::ROR32mi_EVEX,\n    llvm::X86::ROR32mi_NF,\n    llvm::X86::SAR32m1,\n    llvm::X86::SAR32m1_EVEX,\n    llvm::X86::SAR32m1_NF,\n    llvm::X86::SAR32mCL,\n    llvm::X86::SAR32mCL_EVEX,\n    llvm::X86::SAR32mCL_NF,\n    llvm::X86::SAR32mi,\n    llvm::X86::SAR32mi_EVEX,\n    llvm::X86::SAR32mi_NF,\n    llvm::X86::SBB32mi,\n    llvm::X86::SBB32mi8,\n    llvm::X86::SBB32mi8_EVEX,\n    llvm::X86::SBB32mi_EVEX,\n    llvm::X86::SBB32mr,\n    llvm::X86::SBB32mr_EVEX,\n    llvm::X86::SHL32m1,\n    llvm::X86::SHL32m1_EVEX,\n    llvm::X86::SHL32m1_NF,\n    llvm::X86::SHL32mCL,\n    llvm::X86::SHL32mCL_EVEX,\n    llvm::X86::SHL32mCL_NF,\n    llvm::X86::SHL32mi,\n    llvm::X86::SHL32mi_EVEX,\n    llvm::X86::SHL32mi_NF,\n    llvm::X86::SHLD32mrCL,\n    llvm::X86::SHLD32mrCL_EVEX,\n    llvm::X86::SHLD32mrCL_NF,\n    llvm::X86::SHLD32mri8,\n    llvm::X86::SHLD32mri8_EVEX,\n    llvm::X86::SHLD32mri8_NF,\n    llvm::X86::SHR32m1,\n    llvm::X86::SHR32m1_EVEX,\n    llvm::X86::SHR32m1_NF,\n    llvm::X86::SHR32mCL,\n    llvm::X86::SHR32mCL_EVEX,\n    llvm::X86::SHR32mCL_NF,\n    llvm::X86::SHR32mi,\n    llvm::X86::SHR32mi_EVEX,\n    llvm::X86::SHR32mi_NF,\n    llvm::X86::SHRD32mrCL,\n    llvm::X86::SHRD32mrCL_EVEX,\n    llvm::X86::SHRD32mrCL_NF,\n    llvm::X86::SHRD32mri8,\n    llvm::X86::SHRD32mri8_EVEX,\n    llvm::X86::SHRD32mri8_NF,\n    llvm::X86::STMXCSR,\n    llvm::X86::STOSL,\n    llvm::X86::ST_F32m,\n    llvm::X86::ST_FP32m,\n    llvm::X86::SUB32mi,\n    llvm::X86::SUB32mi8,\n    llvm::X86::SUB32mi8_EVEX,\n    llvm::X86::SUB32mi8_NF,\n    llvm::X86::SUB32mi_EVEX,\n    llvm::X86::SUB32mi_NF,\n    llvm::X86::SUB32mr,\n    llvm::X86::SUB32mr_EVEX,\n    llvm::X86::SUB32mr_NF,\n    llvm::X86::VEXTRACTPSmr,\n    llvm::X86::VMOVPDI2DImr,\n    llvm::X86::VMOVSSmr,\n    llvm::X86::VPEXTRDmr,\n    llvm::X86::VSTMXCSR,\n    llvm::X86::XADD32rm,\n    llvm::X86::XCHG32rm,\n    llvm::X86::XOR32mi,\n    llvm::X86::XOR32mi8,\n    llvm::X86::XOR32mi8_EVEX,\n    llvm::X86::XOR32mi8_NF,\n    llvm::X86::XOR32mi_EVEX,\n    llvm::X86::XOR32mi_NF,\n    llvm::X86::XOR32mr,\n    llvm::X86::XOR32mr_EVEX,\n    llvm::X86::XOR32mr_NF,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_32_SIZE = sizeof(WRITE_32) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_64[] = {\n    // clang-format off\n    llvm::X86::AADD64mr,\n    llvm::X86::AADD64mr_EVEX,\n    llvm::X86::AAND64mr,\n    llvm::X86::AAND64mr_EVEX,\n    llvm::X86::ADC64mi32,\n    llvm::X86::ADC64mi32_EVEX,\n    llvm::X86::ADC64mi8,\n    llvm::X86::ADC64mi8_EVEX,\n    llvm::X86::ADC64mr,\n    llvm::X86::ADC64mr_EVEX,\n    llvm::X86::ADD64mi32,\n    llvm::X86::ADD64mi32_EVEX,\n    llvm::X86::ADD64mi32_NF,\n    llvm::X86::ADD64mi8,\n    llvm::X86::ADD64mi8_EVEX,\n    llvm::X86::ADD64mi8_NF,\n    llvm::X86::ADD64mr,\n    llvm::X86::ADD64mr_EVEX,\n    llvm::X86::ADD64mr_NF,\n    llvm::X86::AND64mi32,\n    llvm::X86::AND64mi32_EVEX,\n    llvm::X86::AND64mi32_NF,\n    llvm::X86::AND64mi8,\n    llvm::X86::AND64mi8_EVEX,\n    llvm::X86::AND64mi8_NF,\n    llvm::X86::AND64mr,\n    llvm::X86::AND64mr_EVEX,\n    llvm::X86::AND64mr_NF,\n    llvm::X86::AOR64mr,\n    llvm::X86::AOR64mr_EVEX,\n    llvm::X86::AXOR64mr,\n    llvm::X86::AXOR64mr_EVEX,\n    llvm::X86::BTC64mi8,\n    llvm::X86::BTC64mr,\n    llvm::X86::BTR64mi8,\n    llvm::X86::BTR64mr,\n    llvm::X86::BTS64mi8,\n    llvm::X86::BTS64mr,\n    llvm::X86::CFCMOV64mr,\n    llvm::X86::CMPCCXADDmr64,\n    llvm::X86::CMPCCXADDmr64_EVEX,\n    llvm::X86::CMPXCHG64rm,\n    llvm::X86::CMPXCHG8B,\n    llvm::X86::DEC64m,\n    llvm::X86::DEC64m_EVEX,\n    llvm::X86::DEC64m_NF,\n    llvm::X86::INC64m,\n    llvm::X86::INC64m_EVEX,\n    llvm::X86::INC64m_NF,\n    llvm::X86::ISTT_FP64m,\n    llvm::X86::IST_FP64m,\n    llvm::X86::KMOVQmk,\n    llvm::X86::KMOVQmk_EVEX,\n    llvm::X86::LCMPXCHG64,\n    llvm::X86::LCMPXCHG8B,\n    llvm::X86::LOCK_ADD64mi32,\n    llvm::X86::LOCK_ADD64mi8,\n    llvm::X86::LOCK_ADD64mr,\n    llvm::X86::LOCK_AND64mi32,\n    llvm::X86::LOCK_AND64mi8,\n    llvm::X86::LOCK_AND64mr,\n    llvm::X86::LOCK_BTC64m,\n    llvm::X86::LOCK_BTR64m,\n    llvm::X86::LOCK_BTS64m,\n    llvm::X86::LOCK_DEC64m,\n    llvm::X86::LOCK_INC64m,\n    llvm::X86::LOCK_OR64mi32,\n    llvm::X86::LOCK_OR64mi8,\n    llvm::X86::LOCK_OR64mr,\n    llvm::X86::LOCK_SUB64mi32,\n    llvm::X86::LOCK_SUB64mi8,\n    llvm::X86::LOCK_SUB64mr,\n    llvm::X86::LOCK_XOR64mi32,\n    llvm::X86::LOCK_XOR64mi8,\n    llvm::X86::LOCK_XOR64mr,\n    llvm::X86::MMX_MASKMOVQ,\n    llvm::X86::MMX_MASKMOVQ64,\n    llvm::X86::MMX_MOVNTQmr,\n    llvm::X86::MMX_MOVQ64mr,\n    llvm::X86::MOV64mi32,\n    llvm::X86::MOV64mr,\n    llvm::X86::MOV64o32a,\n    llvm::X86::MOV64o64a,\n    llvm::X86::MOVBE64mr,\n    llvm::X86::MOVBE64mr_EVEX,\n    llvm::X86::MOVDIRI64,\n    llvm::X86::MOVDIRI64_EVEX,\n    llvm::X86::MOVHPDmr,\n    llvm::X86::MOVHPSmr,\n    llvm::X86::MOVLPDmr,\n    llvm::X86::MOVLPSmr,\n    llvm::X86::MOVNTI_64mr,\n    llvm::X86::MOVNTSD,\n    llvm::X86::MOVPQI2QImr,\n    llvm::X86::MOVSDmr,\n    llvm::X86::MOVSQ,\n    llvm::X86::NEG64m,\n    llvm::X86::NEG64m_EVEX,\n    llvm::X86::NEG64m_NF,\n    llvm::X86::NOT64m,\n    llvm::X86::NOT64m_EVEX,\n    llvm::X86::OR64mi32,\n    llvm::X86::OR64mi32_EVEX,\n    llvm::X86::OR64mi32_NF,\n    llvm::X86::OR64mi8,\n    llvm::X86::OR64mi8_EVEX,\n    llvm::X86::OR64mi8_NF,\n    llvm::X86::OR64mr,\n    llvm::X86::OR64mr_EVEX,\n    llvm::X86::OR64mr_NF,\n    llvm::X86::PEXTRQmr,\n    llvm::X86::POP64rmm,\n    llvm::X86::RCL64m1,\n    llvm::X86::RCL64m1_EVEX,\n    llvm::X86::RCL64mCL,\n    llvm::X86::RCL64mCL_EVEX,\n    llvm::X86::RCL64mi,\n    llvm::X86::RCL64mi_EVEX,\n    llvm::X86::RCR64m1,\n    llvm::X86::RCR64m1_EVEX,\n    llvm::X86::RCR64mCL,\n    llvm::X86::RCR64mCL_EVEX,\n    llvm::X86::RCR64mi,\n    llvm::X86::RCR64mi_EVEX,\n    llvm::X86::ROL64m1,\n    llvm::X86::ROL64m1_EVEX,\n    llvm::X86::ROL64m1_NF,\n    llvm::X86::ROL64mCL,\n    llvm::X86::ROL64mCL_EVEX,\n    llvm::X86::ROL64mCL_NF,\n    llvm::X86::ROL64mi,\n    llvm::X86::ROL64mi_EVEX,\n    llvm::X86::ROL64mi_NF,\n    llvm::X86::ROR64m1,\n    llvm::X86::ROR64m1_EVEX,\n    llvm::X86::ROR64m1_NF,\n    llvm::X86::ROR64mCL,\n    llvm::X86::ROR64mCL_EVEX,\n    llvm::X86::ROR64mCL_NF,\n    llvm::X86::ROR64mi,\n    llvm::X86::ROR64mi_EVEX,\n    llvm::X86::ROR64mi_NF,\n    llvm::X86::SAR64m1,\n    llvm::X86::SAR64m1_EVEX,\n    llvm::X86::SAR64m1_NF,\n    llvm::X86::SAR64mCL,\n    llvm::X86::SAR64mCL_EVEX,\n    llvm::X86::SAR64mCL_NF,\n    llvm::X86::SAR64mi,\n    llvm::X86::SAR64mi_EVEX,\n    llvm::X86::SAR64mi_NF,\n    llvm::X86::SBB64mi32,\n    llvm::X86::SBB64mi32_EVEX,\n    llvm::X86::SBB64mi8,\n    llvm::X86::SBB64mi8_EVEX,\n    llvm::X86::SBB64mr,\n    llvm::X86::SBB64mr_EVEX,\n    llvm::X86::SHL64m1,\n    llvm::X86::SHL64m1_EVEX,\n    llvm::X86::SHL64m1_NF,\n    llvm::X86::SHL64mCL,\n    llvm::X86::SHL64mCL_EVEX,\n    llvm::X86::SHL64mCL_NF,\n    llvm::X86::SHL64mi,\n    llvm::X86::SHL64mi_EVEX,\n    llvm::X86::SHL64mi_NF,\n    llvm::X86::SHLD64mrCL,\n    llvm::X86::SHLD64mrCL_EVEX,\n    llvm::X86::SHLD64mrCL_NF,\n    llvm::X86::SHLD64mri8,\n    llvm::X86::SHLD64mri8_EVEX,\n    llvm::X86::SHLD64mri8_NF,\n    llvm::X86::SHR64m1,\n    llvm::X86::SHR64m1_EVEX,\n    llvm::X86::SHR64m1_NF,\n    llvm::X86::SHR64mCL,\n    llvm::X86::SHR64mCL_EVEX,\n    llvm::X86::SHR64mCL_NF,\n    llvm::X86::SHR64mi,\n    llvm::X86::SHR64mi_EVEX,\n    llvm::X86::SHR64mi_NF,\n    llvm::X86::SHRD64mrCL,\n    llvm::X86::SHRD64mrCL_EVEX,\n    llvm::X86::SHRD64mrCL_NF,\n    llvm::X86::SHRD64mri8,\n    llvm::X86::SHRD64mri8_EVEX,\n    llvm::X86::SHRD64mri8_NF,\n    llvm::X86::STOSQ,\n    llvm::X86::ST_F64m,\n    llvm::X86::ST_FP64m,\n    llvm::X86::SUB64mi32,\n    llvm::X86::SUB64mi32_EVEX,\n    llvm::X86::SUB64mi32_NF,\n    llvm::X86::SUB64mi8,\n    llvm::X86::SUB64mi8_EVEX,\n    llvm::X86::SUB64mi8_NF,\n    llvm::X86::SUB64mr,\n    llvm::X86::SUB64mr_EVEX,\n    llvm::X86::SUB64mr_NF,\n    llvm::X86::VCVTPS2PHmr,\n    llvm::X86::VMOVHPDmr,\n    llvm::X86::VMOVHPSmr,\n    llvm::X86::VMOVLPDmr,\n    llvm::X86::VMOVLPSmr,\n    llvm::X86::VMOVPQI2QIZmr,\n    llvm::X86::VMOVPQI2QImr,\n    llvm::X86::VMOVPQIto64Zmr,\n    llvm::X86::VMOVSDmr,\n    llvm::X86::VPEXTRQmr,\n    llvm::X86::XADD64rm,\n    llvm::X86::XCHG64rm,\n    llvm::X86::XOR64mi32,\n    llvm::X86::XOR64mi32_EVEX,\n    llvm::X86::XOR64mi32_NF,\n    llvm::X86::XOR64mi8,\n    llvm::X86::XOR64mi8_EVEX,\n    llvm::X86::XOR64mi8_NF,\n    llvm::X86::XOR64mr,\n    llvm::X86::XOR64mr_EVEX,\n    llvm::X86::XOR64mr_NF,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_64_SIZE = sizeof(WRITE_64) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_80[] = {\n    // clang-format off\n    llvm::X86::FBSTPm,\n    llvm::X86::ST_FP80m,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_80_SIZE = sizeof(WRITE_80) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_128[] = {\n    // clang-format off\n    llvm::X86::CMPXCHG16B,\n    llvm::X86::LCMPXCHG16B,\n    llvm::X86::MASKMOVDQU,\n    llvm::X86::MASKMOVDQU64,\n    llvm::X86::MOVAPDmr,\n    llvm::X86::MOVAPSmr,\n    llvm::X86::MOVDQAmr,\n    llvm::X86::MOVDQUmr,\n    llvm::X86::MOVNTDQmr,\n    llvm::X86::MOVNTPDmr,\n    llvm::X86::MOVNTPSmr,\n    llvm::X86::MOVUPDmr,\n    llvm::X86::MOVUPSmr,\n    llvm::X86::VCVTPS2PHYmr,\n    llvm::X86::VEXTRACTF128mr,\n    llvm::X86::VEXTRACTI128mr,\n    llvm::X86::VMASKMOVDQU,\n    llvm::X86::VMASKMOVDQU64,\n    llvm::X86::VMASKMOVPDmr,\n    llvm::X86::VMASKMOVPSmr,\n    llvm::X86::VMOVAPDmr,\n    llvm::X86::VMOVAPSmr,\n    llvm::X86::VMOVDQAmr,\n    llvm::X86::VMOVDQUmr,\n    llvm::X86::VMOVNTDQmr,\n    llvm::X86::VMOVNTPDmr,\n    llvm::X86::VMOVNTPSmr,\n    llvm::X86::VMOVUPDmr,\n    llvm::X86::VMOVUPSmr,\n    llvm::X86::VPMASKMOVDmr,\n    llvm::X86::VPMASKMOVQmr,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_128_SIZE = sizeof(WRITE_128) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_224[] = {\n    // clang-format off\n    llvm::X86::FSTENVm,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_224_SIZE = sizeof(WRITE_224) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_256[] = {\n    // clang-format off\n    llvm::X86::VMASKMOVPDYmr,\n    llvm::X86::VMASKMOVPSYmr,\n    llvm::X86::VMOVAPDYmr,\n    llvm::X86::VMOVAPSYmr,\n    llvm::X86::VMOVDQAYmr,\n    llvm::X86::VMOVDQUYmr,\n    llvm::X86::VMOVNTDQYmr,\n    llvm::X86::VMOVNTPDYmr,\n    llvm::X86::VMOVNTPSYmr,\n    llvm::X86::VMOVUPDYmr,\n    llvm::X86::VMOVUPSYmr,\n    llvm::X86::VPMASKMOVDYmr,\n    llvm::X86::VPMASKMOVQYmr,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_256_SIZE = sizeof(WRITE_256) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_512[] = {\n    // clang-format off\n    llvm::X86::STTILECFG,\n    llvm::X86::STTILECFG_EVEX,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_512_SIZE = sizeof(WRITE_512) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_864[] = {\n    // clang-format off\n    llvm::X86::FSAVEm,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_864_SIZE = sizeof(WRITE_864) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_4096[] = {\n    // clang-format off\n    llvm::X86::FXSAVE,\n    llvm::X86::FXSAVE64,\n    llvm::X86::MOVDIR64B16,\n    llvm::X86::MOVDIR64B32,\n    llvm::X86::MOVDIR64B32_EVEX,\n    llvm::X86::MOVDIR64B64,\n    llvm::X86::MOVDIR64B64_EVEX,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_4096_SIZE = sizeof(WRITE_4096) / sizeof(unsigned);\n\nconstexpr unsigned WRITE_4608[] = {\n    // clang-format off\n    llvm::X86::XSAVE,\n    llvm::X86::XSAVE64,\n    llvm::X86::XSAVEC,\n    llvm::X86::XSAVEC64,\n    llvm::X86::XSAVEOPT,\n    llvm::X86::XSAVEOPT64,\n    llvm::X86::XSAVES,\n    llvm::X86::XSAVES64,\n    // clang-format on\n};\n\nconstexpr size_t WRITE_4608_SIZE = sizeof(WRITE_4608) / sizeof(unsigned);\n\nconstexpr unsigned STACK_WRITE_16[] = {\n    // clang-format off\n    llvm::X86::PUSH16i,\n    llvm::X86::PUSH16i8,\n    llvm::X86::PUSH16r,\n    llvm::X86::PUSH16rmm,\n    llvm::X86::PUSH16rmr,\n    llvm::X86::PUSHF16,\n    // clang-format on\n};\n\nconstexpr size_t STACK_WRITE_16_SIZE =\n    sizeof(STACK_WRITE_16) / sizeof(unsigned);\n\nconstexpr unsigned STACK_WRITE_32[] = {\n// clang-format off\n#ifdef QBDI_ARCH_X86\n    llvm::X86::CALL16m,\n    llvm::X86::CALL16m_NT,\n    llvm::X86::CALL16r,\n    llvm::X86::CALL16r_NT,\n    llvm::X86::CALL32m,\n    llvm::X86::CALL32m_NT,\n    llvm::X86::CALL32r,\n    llvm::X86::CALL32r_NT,\n    llvm::X86::CALL64m,\n    llvm::X86::CALL64m_NT,\n    llvm::X86::CALL64pcrel32,\n    llvm::X86::CALL64r,\n    llvm::X86::CALL64r_NT,\n    llvm::X86::CALLpcrel16,\n    llvm::X86::CALLpcrel32,\n    llvm::X86::ENTER,\n#endif\n    llvm::X86::PUSH32i,\n    llvm::X86::PUSH32i8,\n    llvm::X86::PUSH32r,\n    llvm::X86::PUSH32rmm,\n    llvm::X86::PUSH32rmr,\n    llvm::X86::PUSHF32,\n    // clang-format on\n};\n\nconstexpr size_t STACK_WRITE_32_SIZE =\n    sizeof(STACK_WRITE_32) / sizeof(unsigned);\n\nconstexpr unsigned STACK_WRITE_64[] = {\n// clang-format off\n#ifdef QBDI_ARCH_X86_64\n    llvm::X86::CALL16m,\n    llvm::X86::CALL16m_NT,\n    llvm::X86::CALL16r,\n    llvm::X86::CALL16r_NT,\n    llvm::X86::CALL32m,\n    llvm::X86::CALL32m_NT,\n    llvm::X86::CALL32r,\n    llvm::X86::CALL32r_NT,\n    llvm::X86::CALL64m,\n    llvm::X86::CALL64m_NT,\n    llvm::X86::CALL64pcrel32,\n    llvm::X86::CALL64r,\n    llvm::X86::CALL64r_NT,\n    llvm::X86::CALLpcrel16,\n    llvm::X86::CALLpcrel32,\n    llvm::X86::ENTER,\n#endif\n    llvm::X86::PUSH64i32,\n    llvm::X86::PUSH64i8,\n    llvm::X86::PUSH64r,\n    llvm::X86::PUSH64rmm,\n    llvm::X86::PUSH64rmr,\n    llvm::X86::PUSHF64,\n    llvm::X86::PUSHP64r,\n    // clang-format on\n};\n\nconstexpr size_t STACK_WRITE_64_SIZE =\n    sizeof(STACK_WRITE_64) / sizeof(unsigned);\n\nconstexpr unsigned STACK_WRITE_128[] = {\n    // clang-format off\n    llvm::X86::PUSH2,\n    llvm::X86::PUSH2P,\n    llvm::X86::PUSHA16,\n    // clang-format on\n};\n\nconstexpr size_t STACK_WRITE_128_SIZE =\n    sizeof(STACK_WRITE_128) / sizeof(unsigned);\n\nconstexpr unsigned STACK_WRITE_256[] = {\n    // clang-format off\n    llvm::X86::PUSHA32,\n    // clang-format on\n};\n\nconstexpr size_t STACK_WRITE_256_SIZE =\n    sizeof(STACK_WRITE_256) / sizeof(unsigned);\n\nconstexpr unsigned STACK_READ_16[] = {\n    // clang-format off\n    llvm::X86::POP16r,\n    llvm::X86::POP16rmm,\n    llvm::X86::POP16rmr,\n    llvm::X86::POPF16,\n    // clang-format on\n};\n\nconstexpr size_t STACK_READ_16_SIZE = sizeof(STACK_READ_16) / sizeof(unsigned);\n\nconstexpr unsigned STACK_READ_32[] = {\n// clang-format off\n#ifdef QBDI_ARCH_X86\n    llvm::X86::LEAVE,\n    llvm::X86::LRETI32,\n    llvm::X86::LRETI64,\n    llvm::X86::LRETI16,\n    llvm::X86::LRET32,\n    llvm::X86::LRET64,\n    llvm::X86::LRET16,\n#endif\n    llvm::X86::POP32r,\n    llvm::X86::POP32rmm,\n    llvm::X86::POP32rmr,\n    llvm::X86::POPF32,\n#ifdef QBDI_ARCH_X86\n    llvm::X86::RETI32,\n    llvm::X86::RETI64,\n    llvm::X86::RETI16,\n    llvm::X86::RET32,\n    llvm::X86::RET64,\n    llvm::X86::RET16,\n#endif\n    // clang-format on\n};\n\nconstexpr size_t STACK_READ_32_SIZE = sizeof(STACK_READ_32) / sizeof(unsigned);\n\nconstexpr unsigned STACK_READ_64[] = {\n// clang-format off\n#ifdef QBDI_ARCH_X86_64\n    llvm::X86::LEAVE,\n#endif\n    llvm::X86::LEAVE64,\n#ifdef QBDI_ARCH_X86_64\n    llvm::X86::LRETI32,\n    llvm::X86::LRETI64,\n    llvm::X86::LRETI16,\n    llvm::X86::LRET32,\n    llvm::X86::LRET64,\n    llvm::X86::LRET16,\n#endif\n    llvm::X86::POP64r,\n    llvm::X86::POP64rmm,\n    llvm::X86::POP64rmr,\n    llvm::X86::POPF64,\n    llvm::X86::POPP64r,\n#ifdef QBDI_ARCH_X86_64\n    llvm::X86::RETI32,\n    llvm::X86::RETI64,\n    llvm::X86::RETI16,\n    llvm::X86::RET32,\n    llvm::X86::RET64,\n    llvm::X86::RET16,\n#endif\n    // clang-format on\n};\n\nconstexpr size_t STACK_READ_64_SIZE = sizeof(STACK_READ_64) / sizeof(unsigned);\n\nconstexpr unsigned STACK_READ_128[] = {\n    // clang-format off\n    llvm::X86::POP2,\n    llvm::X86::POP2P,\n    llvm::X86::POPA16,\n    // clang-format on\n};\n\nconstexpr size_t STACK_READ_128_SIZE =\n    sizeof(STACK_READ_128) / sizeof(unsigned);\n\nconstexpr unsigned STACK_READ_256[] = {\n    llvm::X86::POPA32,\n};\n\nconstexpr size_t STACK_READ_256_SIZE =\n    sizeof(STACK_READ_256) / sizeof(unsigned);\n\nconstexpr unsigned MIN_SIZE_READ[] = {\n    // clang-format off\n    llvm::X86::XRSTOR,\n    llvm::X86::XRSTOR64,\n    llvm::X86::XRSTORS,\n    llvm::X86::XRSTORS64,\n    llvm::X86::XSAVE,\n    llvm::X86::XSAVE64,\n    llvm::X86::XSAVEC,\n    llvm::X86::XSAVEC64,\n    llvm::X86::XSAVEOPT,\n    llvm::X86::XSAVEOPT64,\n    llvm::X86::XSAVES,\n    llvm::X86::XSAVES64,\n    // clang-format on\n};\n\nconstexpr size_t MIN_SIZE_READ_SIZE = sizeof(MIN_SIZE_READ) / sizeof(unsigned);\n\nconstexpr unsigned MIN_SIZE_WRITE[] = {\n    // clang-format off\n    llvm::X86::XSAVE,\n    llvm::X86::XSAVE64,\n    llvm::X86::XSAVEC,\n    llvm::X86::XSAVEC64,\n    llvm::X86::XSAVEOPT,\n    llvm::X86::XSAVEOPT64,\n    llvm::X86::XSAVES,\n    llvm::X86::XSAVES64,\n    // clang-format on\n};\n\nconstexpr size_t MIN_SIZE_WRITE_SIZE =\n    sizeof(MIN_SIZE_WRITE) / sizeof(unsigned);\n\n/* Highest 16 bits are the write access, lowest 16 bits are the read access. For\n * each 16 bits part: the highest bit stores if the access is a stack access or\n * not while the lowest 12 bits store the unsigned access size in bytes (thus up\n * to 4095 bytes). A size of 0 means no access.\n *\n * ------------------------------------------------------------------\n * | Ox1f                        WRITE ACCESS                  0x1c |\n * ------------------------------------------------------------------\n * | 1 bit stack access flag | 1 bit minimum size | 2 bits reserved |\n * ------------------------------------------------------------------\n *\n * --------------------------------\n * | 0x1b   WRITE ACCESS     0x10 |\n * --------------------------------\n * | 12 bits unsigned access size |\n * --------------------------------\n *\n *\n * ------------------------------------------------------------------\n * | 0xf                           READ ACCESS                  0xc |\n * ------------------------------------------------------------------\n * | 1 bit stack access flag | 1 bit minimum size | 2 bits reserved |\n * ------------------------------------------------------------------\n *\n * --------------------------------\n * | 0xb     READ ACCESS     0x10 |\n * --------------------------------\n * | 12 bits unsigned access size |\n * --------------------------------\n */\n\nconstexpr uint32_t WRITE_POSITION = 16;\nconstexpr uint32_t STACK_ACCESS_FLAG = 0x8000;\nconstexpr uint32_t ACCESS_MIN_SIZE_FLAG = 0x4000;\nconstexpr uint32_t READ(uint32_t s) { return s & 0xfff; }\nconstexpr uint32_t WRITE(uint32_t s) { return (s & 0xfff) << WRITE_POSITION; }\nconstexpr uint32_t STACK_READ(uint32_t s) {\n  return STACK_ACCESS_FLAG | READ(s);\n}\nconstexpr uint32_t STACK_WRITE(uint32_t s) {\n  return ((STACK_ACCESS_FLAG) << WRITE_POSITION) | WRITE(s);\n}\nconstexpr uint32_t GET_READ_SIZE(uint32_t v) { return v & 0xfff; }\nconstexpr uint32_t GET_WRITE_SIZE(uint32_t v) {\n  return (v >> WRITE_POSITION) & 0xfff;\n}\nconstexpr uint32_t IS_STACK_READ(uint32_t v) {\n  return (v & STACK_ACCESS_FLAG) == STACK_ACCESS_FLAG;\n}\nconstexpr uint32_t IS_STACK_WRITE(uint32_t v) {\n  return ((v >> WRITE_POSITION) & STACK_ACCESS_FLAG) == STACK_ACCESS_FLAG;\n}\nconstexpr uint32_t IS_MIN_SIZE_READ(uint32_t v) {\n  return (v & ACCESS_MIN_SIZE_FLAG) == ACCESS_MIN_SIZE_FLAG;\n}\nconstexpr uint32_t IS_MIN_SIZE_WRITE(uint32_t v) {\n  return ((v >> WRITE_POSITION) & ACCESS_MIN_SIZE_FLAG) == ACCESS_MIN_SIZE_FLAG;\n}\n\nstruct MemAccessArray {\n  uint32_t arr[llvm::X86::INSTRUCTION_LIST_END] = {0};\n\n  constexpr inline void _initMemAccessRead(const unsigned buff[],\n                                           size_t buff_size, uint32_t len) {\n    for (size_t i = 0; i < buff_size; i++) {\n      arr[buff[i]] |= READ(len);\n    }\n  }\n\n  constexpr inline void _initMemAccessWrite(const unsigned buff[],\n                                            size_t buff_size, uint32_t len) {\n    for (size_t i = 0; i < buff_size; i++) {\n      arr[buff[i]] |= WRITE(len);\n    }\n  }\n\n  constexpr inline void _initMemAccessStackRead(const unsigned buff[],\n                                                size_t buff_size,\n                                                uint32_t len) {\n    for (size_t i = 0; i < buff_size; i++) {\n      arr[buff[i]] |= STACK_READ(len);\n    }\n  }\n\n  constexpr inline void _initMemAccessStackWrite(const unsigned buff[],\n                                                 size_t buff_size,\n                                                 uint32_t len) {\n    for (size_t i = 0; i < buff_size; i++) {\n      arr[buff[i]] |= STACK_WRITE(len);\n    }\n  }\n\n  constexpr MemAccessArray() {\n    // read\n    _initMemAccessRead(READ_8, READ_8_SIZE, 1);\n    _initMemAccessRead(READ_16, READ_16_SIZE, 2);\n    _initMemAccessRead(READ_32, READ_32_SIZE, 4);\n    _initMemAccessRead(READ_64, READ_64_SIZE, 8);\n    _initMemAccessRead(READ_80, READ_80_SIZE, 10);\n    _initMemAccessRead(READ_128, READ_128_SIZE, 16);\n    _initMemAccessRead(READ_224, READ_224_SIZE, 28);\n    _initMemAccessRead(READ_256, READ_256_SIZE, 32);\n    _initMemAccessRead(READ_384, READ_384_SIZE, 48);\n    _initMemAccessRead(READ_512, READ_512_SIZE, 64);\n    _initMemAccessRead(READ_864, READ_864_SIZE, 108);\n    _initMemAccessRead(READ_4096, READ_4096_SIZE, 512);\n    _initMemAccessRead(READ_4608, READ_4608_SIZE, 576);\n    // write\n    _initMemAccessWrite(WRITE_8, WRITE_8_SIZE, 1);\n    _initMemAccessWrite(WRITE_16, WRITE_16_SIZE, 2);\n    _initMemAccessWrite(WRITE_32, WRITE_32_SIZE, 4);\n    _initMemAccessWrite(WRITE_64, WRITE_64_SIZE, 8);\n    _initMemAccessWrite(WRITE_80, WRITE_80_SIZE, 10);\n    _initMemAccessWrite(WRITE_128, WRITE_128_SIZE, 16);\n    _initMemAccessWrite(WRITE_224, WRITE_224_SIZE, 28);\n    _initMemAccessWrite(WRITE_256, WRITE_256_SIZE, 32);\n    _initMemAccessWrite(WRITE_512, WRITE_512_SIZE, 64);\n    _initMemAccessWrite(WRITE_864, WRITE_864_SIZE, 108);\n    _initMemAccessWrite(WRITE_4096, WRITE_4096_SIZE, 512);\n    _initMemAccessWrite(WRITE_4608, WRITE_4608_SIZE, 576);\n    // read stack\n    _initMemAccessStackRead(STACK_READ_16, STACK_READ_16_SIZE, 2);\n    _initMemAccessStackRead(STACK_READ_32, STACK_READ_32_SIZE, 4);\n    _initMemAccessStackRead(STACK_READ_64, STACK_READ_64_SIZE, 8);\n    _initMemAccessStackRead(STACK_READ_128, STACK_READ_128_SIZE, 16);\n    _initMemAccessStackRead(STACK_READ_256, STACK_READ_256_SIZE, 32);\n    // write stack\n    _initMemAccessStackWrite(STACK_WRITE_16, STACK_WRITE_16_SIZE, 2);\n    _initMemAccessStackWrite(STACK_WRITE_32, STACK_WRITE_32_SIZE, 4);\n    _initMemAccessStackWrite(STACK_WRITE_64, STACK_WRITE_64_SIZE, 8);\n    _initMemAccessStackWrite(STACK_WRITE_128, STACK_WRITE_128_SIZE, 16);\n    _initMemAccessStackWrite(STACK_WRITE_256, STACK_WRITE_256_SIZE, 32);\n    // min size read\n    for (size_t i = 0; i < MIN_SIZE_READ_SIZE; i++) {\n      arr[MIN_SIZE_READ[i]] |= ACCESS_MIN_SIZE_FLAG;\n    }\n    // min size write\n    for (size_t i = 0; i < MIN_SIZE_WRITE_SIZE; i++) {\n      arr[MIN_SIZE_WRITE[i]] |= (ACCESS_MIN_SIZE_FLAG << WRITE_POSITION);\n    }\n  }\n\n#if CHECK_INSTINFO_TABLE\n  void check_table(const unsigned buff[], size_t buff_size, uint32_t value,\n                   uint32_t mask) const {\n    for (size_t i = 0; i < buff_size; i++) {\n      if ((arr[buff[i]] & mask) != value) {\n        fprintf(stderr,\n                \"[MemAccessArray::check_table], opcode %d, mask %x, expected \"\n                \"%x, found %x\\n\",\n                buff[i], mask, value, (arr[buff[i]] & mask));\n        abort();\n      }\n    }\n  }\n\n  int check() const {\n    // read\n    check_table(READ_8, READ_8_SIZE, READ(1), 0x8fff);\n    check_table(READ_16, READ_16_SIZE, READ(2), 0x8fff);\n    check_table(READ_32, READ_32_SIZE, READ(4), 0x8fff);\n    check_table(READ_64, READ_64_SIZE, READ(8), 0x8fff);\n    check_table(READ_80, READ_80_SIZE, READ(10), 0x8fff);\n    check_table(READ_128, READ_128_SIZE, READ(16), 0x8fff);\n    check_table(READ_224, READ_224_SIZE, READ(28), 0x8fff);\n    check_table(READ_256, READ_256_SIZE, READ(32), 0x8fff);\n    check_table(READ_384, READ_384_SIZE, READ(48), 0x8fff);\n    check_table(READ_512, READ_512_SIZE, READ(64), 0x8fff);\n    check_table(READ_864, READ_864_SIZE, READ(108), 0x8fff);\n    check_table(READ_4096, READ_4096_SIZE, READ(512), 0x8fff);\n    check_table(READ_4608, READ_4608_SIZE, READ(576), 0x8fff);\n    // write\n    check_table(WRITE_8, WRITE_8_SIZE, WRITE(1), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_16, WRITE_16_SIZE, WRITE(2), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_32, WRITE_32_SIZE, WRITE(4), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_64, WRITE_64_SIZE, WRITE(8), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_80, WRITE_80_SIZE, WRITE(10), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_128, WRITE_128_SIZE, WRITE(16), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_224, WRITE_224_SIZE, WRITE(28), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_256, WRITE_256_SIZE, WRITE(32), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_512, WRITE_512_SIZE, WRITE(64), 0x8fff << WRITE_POSITION);\n    check_table(WRITE_864, WRITE_864_SIZE, WRITE(108),\n                0x8fff << WRITE_POSITION);\n    check_table(WRITE_4096, WRITE_4096_SIZE, WRITE(512),\n                0x8fff << WRITE_POSITION);\n    check_table(WRITE_4608, WRITE_4608_SIZE, WRITE(576),\n                0x8fff << WRITE_POSITION);\n    // read stack\n    check_table(STACK_READ_16, STACK_READ_16_SIZE, STACK_READ(2), 0x8fff);\n    check_table(STACK_READ_32, STACK_READ_32_SIZE, STACK_READ(4), 0x8fff);\n    check_table(STACK_READ_64, STACK_READ_64_SIZE, STACK_READ(8), 0x8fff);\n    check_table(STACK_READ_128, STACK_READ_128_SIZE, STACK_READ(16), 0x8fff);\n    check_table(STACK_READ_256, STACK_READ_256_SIZE, STACK_READ(32), 0x8fff);\n    // write stack\n    check_table(STACK_WRITE_16, STACK_WRITE_16_SIZE, STACK_WRITE(2),\n                0x8fff << WRITE_POSITION);\n    check_table(STACK_WRITE_32, STACK_WRITE_32_SIZE, STACK_WRITE(4),\n                0x8fff << WRITE_POSITION);\n    check_table(STACK_WRITE_64, STACK_WRITE_64_SIZE, STACK_WRITE(8),\n                0x8fff << WRITE_POSITION);\n    check_table(STACK_WRITE_128, STACK_WRITE_128_SIZE, STACK_WRITE(16),\n                0x8fff << WRITE_POSITION);\n    check_table(STACK_WRITE_256, STACK_WRITE_256_SIZE, STACK_WRITE(32),\n                0x8fff << WRITE_POSITION);\n    // min size read\n    check_table(MIN_SIZE_READ, MIN_SIZE_READ_SIZE, ACCESS_MIN_SIZE_FLAG,\n                ACCESS_MIN_SIZE_FLAG);\n    // min size write\n    check_table(MIN_SIZE_WRITE, MIN_SIZE_WRITE_SIZE,\n                ACCESS_MIN_SIZE_FLAG << WRITE_POSITION,\n                ACCESS_MIN_SIZE_FLAG << WRITE_POSITION);\n    return 0;\n  }\n#endif\n\n  inline uint32_t get(size_t op) const {\n    if (op < llvm::X86::INSTRUCTION_LIST_END) {\n      return arr[op];\n    }\n\n    QBDI_ERROR(\"No opcode {}\", op);\n    return 0;\n  }\n};\n\nstatic constexpr MemAccessArray memAccessCache;\n\n#if CHECK_INSTINFO_TABLE\nstatic int __check_debug = memAccessCache.check();\n#endif\n\n} // anonymous namespace\n\nunsigned getReadSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  return GET_READ_SIZE(memAccessCache.get(inst.getOpcode()));\n}\n\nunsigned getWriteSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  return GET_WRITE_SIZE(memAccessCache.get(inst.getOpcode()));\n}\n\nbool isStackRead(const llvm::MCInst &inst) {\n  return IS_STACK_READ(memAccessCache.get(inst.getOpcode()));\n}\n\nbool isStackWrite(const llvm::MCInst &inst) {\n  return IS_STACK_WRITE(memAccessCache.get(inst.getOpcode()));\n}\n\nbool isMinSizeRead(const llvm::MCInst &inst) {\n  return IS_MIN_SIZE_READ(memAccessCache.get(inst.getOpcode()));\n}\n\nbool isMinSizeWrite(const llvm::MCInst &inst) {\n  return IS_MIN_SIZE_WRITE(memAccessCache.get(inst.getOpcode()));\n}\n\nsword getFixedOperandValue(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                           unsigned index, int64_t value) {\n  return static_cast<rword>(value);\n}\n\nunsigned getImmediateSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  const llvm::MCInstrDesc &desc = llvmcpu.getMCII().get(inst.getOpcode());\n  return llvm::X86II::getSizeOfImm(desc.TSFlags);\n}\n\nbool isDoubleRead(const llvm::MCInst &inst) {\n  switch (inst.getOpcode()) {\n    case llvm::X86::CMPSB:\n    case llvm::X86::CMPSL:\n    case llvm::X86::CMPSQ:\n    case llvm::X86::CMPSW:\n      return true;\n    default:\n      return false;\n  }\n}\n\nbool mayChangeWriteAddr(const llvm::MCInst &inst,\n                        const llvm::MCInstrDesc &desc) {\n\n  switch (desc.TSFlags & llvm::X86II::FormMask) {\n    case llvm::X86II::RawFrmDstSrc:\n    case llvm::X86II::RawFrmDst:\n    case llvm::X86II::RawFrmSrc:\n      return true;\n    default:\n      break;\n  }\n\n  switch (inst.getOpcode()) {\n    case llvm::X86::XCHG8rm:\n    case llvm::X86::XCHG16rm:\n    case llvm::X86::XCHG32rm:\n    case llvm::X86::XCHG64rm:\n    case llvm::X86::CMPXCHG8rm:\n    case llvm::X86::CMPXCHG16rm:\n    case llvm::X86::CMPXCHG32rm:\n    case llvm::X86::CMPXCHG64rm:\n    case llvm::X86::CMPXCHG8B:\n    case llvm::X86::CMPXCHG16B:\n    case llvm::X86::LCMPXCHG8:\n    case llvm::X86::LCMPXCHG16:\n    case llvm::X86::LCMPXCHG32:\n    case llvm::X86::LCMPXCHG64:\n    case llvm::X86::LCMPXCHG8B:\n    case llvm::X86::LCMPXCHG16B:\n      return true;\n    default:\n      return false;\n  }\n}\n\nbool hasREPPrefix(const llvm::MCInst &instr) {\n  return (instr.getFlags() &\n          (llvm::X86::IP_HAS_REPEAT_NE | llvm::X86::IP_HAS_REPEAT)) !=\n         llvm::X86::IP_NO_PREFIX;\n}\n\nbool implicitDSIAccess(const llvm::MCInst &inst,\n                       const llvm::MCInstrDesc &desc) {\n\n  switch (desc.TSFlags & llvm::X86II::FormMask) {\n    case llvm::X86II::RawFrmDstSrc:\n    case llvm::X86II::RawFrmDst:\n    case llvm::X86II::RawFrmSrc:\n      return true;\n    default:\n      break;\n  }\n\n  switch (inst.getOpcode()) {\n    case llvm::X86::MASKMOVDQU:\n    case llvm::X86::MASKMOVDQU64:\n    case llvm::X86::MMX_MASKMOVQ:\n    case llvm::X86::MMX_MASKMOVQ64:\n    case llvm::X86::VMASKMOVDQU:\n    case llvm::X86::VMASKMOVDQU64:\n      return true;\n    default:\n      return false;\n  }\n}\n\nbool unsupportedRead(const llvm::MCInst &inst) {\n\n  switch (inst.getOpcode()) {\n    case llvm::X86::TILELOADD:\n    case llvm::X86::TILELOADDT1:\n    case llvm::X86::TILELOADDT1_EVEX:\n    case llvm::X86::TILELOADD_EVEX:\n    case llvm::X86::VGATHERDPDYrm:\n    case llvm::X86::VGATHERDPDrm:\n    case llvm::X86::VGATHERDPSYrm:\n    case llvm::X86::VGATHERDPSrm:\n    case llvm::X86::VGATHERQPDYrm:\n    case llvm::X86::VGATHERQPDrm:\n    case llvm::X86::VGATHERQPSYrm:\n    case llvm::X86::VGATHERQPSrm:\n    case llvm::X86::VPGATHERDDYrm:\n    case llvm::X86::VPGATHERDDrm:\n    case llvm::X86::VPGATHERDQYrm:\n    case llvm::X86::VPGATHERDQrm:\n    case llvm::X86::VPGATHERQDYrm:\n    case llvm::X86::VPGATHERQDrm:\n    case llvm::X86::VPGATHERQQYrm:\n    case llvm::X86::VPGATHERQQrm:\n      return true;\n    default:\n      return false;\n  }\n}\n\nbool unsupportedWrite(const llvm::MCInst &inst) {\n  switch (inst.getOpcode()) {\n    case llvm::X86::TILESTORED:\n    case llvm::X86::TILESTORED_EVEX:\n      return true;\n    default:\n      return false;\n  }\n}\n\nbool variadicOpsIsWrite(const llvm::MCInst &inst) { return false; }\n\nunsigned getInstSize(const llvm::MCInst &inst, const LLVMCPU &llvmcpu) {\n  return llvmcpu.getMCInstSize(inst);\n}\n\n// TODO\n//  With APX, some memory access may be conditionnal (ie, the memory access\n//  isn't perform and no fault are generated if the condition isn't reach.\n//  The current implementation of Memory Access cannot avoid dump fault when the\n//  condition isn't reach\n// - CFCMOV16rm\n// - CFCMOV16rm_ND\n// - CFCMOV32rm\n// - CFCMOV32rm_ND\n// - CFCMOV64rm\n// - CFCMOV64rm_ND\n// - CFCMOV16mr\n// - CFCMOV32mr\n// - CFCMOV64mr\n\n}; // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/InstInfo_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTINFO_X86_64_H\n#define INSTINFO_X86_64_H\n\nnamespace llvm {\nclass MCInst;\nclass MCInstrDesc;\n} // namespace llvm\n\nnamespace QBDI {\n\nbool isStackRead(const llvm::MCInst &inst);\n\nbool isStackWrite(const llvm::MCInst &inst);\n\nbool isMinSizeRead(const llvm::MCInst &inst);\n\nbool isMinSizeWrite(const llvm::MCInst &inst);\n\nbool isDoubleRead(const llvm::MCInst &inst);\n\n// does the instruction may change the value of register used to address a\n// strore\nbool mayChangeWriteAddr(const llvm::MCInst &inst,\n                        const llvm::MCInstrDesc &desc);\n\nbool hasREPPrefix(const llvm::MCInst &instr);\n\nbool implicitDSIAccess(const llvm::MCInst &inst, const llvm::MCInstrDesc &desc);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/X86_64/InstMetadata_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTMETADATA_X86_64_H\n#define INSTMETADATA_X86_64_H\n\nnamespace QBDI {\n\nstruct InstMetadataArch {\n  // nothing for X86_64\n};\n\n} // namespace QBDI\n\n#endif // INSTMETADATA_X86_64_H\n"
  },
  {
    "path": "src/Patch/X86_64/InstrRules_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stddef.h>\n#include <stdlib.h>\n\n#include \"ExecBlock/Context.h\"\n#include \"Patch/InstrRules.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Patch/X86_64/Layer2_X86_64.h\"\n#include \"Patch/X86_64/RelocatableInst_X86_64.h\"\n\n#include \"QBDI/Config.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\nclass Patch;\n\n/* Generate a series of RelocatableInst which when appended to an\n * instrumentation code trigger a break to host. It receive in argument a\n * temporary reg which will be used for computations then finally restored.\n */\nRelocatableInst::UniquePtrVec getBreakToHost(Reg temp, const Patch &patch,\n                                             bool restore) {\n  RelocatableInst::UniquePtrVec breakToHost;\n\n  QBDI_REQUIRE_ABORT(restore, \"X86 don't have a temporary register\");\n\n  // Use the temporary register to compute RIP + offset which is the address\n  // which will follow this patch and where the execution needs to be resumed\n  if constexpr (is_x86)\n    breakToHost.push_back(SetRegtoPCRel::unique(temp, 22));\n  else\n    breakToHost.push_back(Add(temp, Reg(REG_PC), 19));\n  // Set the selector to this address so the execution can be resumed when the\n  // exec block will be reexecuted\n  append(breakToHost,\n         SaveReg(temp, Offset(offsetof(Context, hostState.selector)))\n             .genReloc(*patch.llvmcpu));\n  // Restore the temporary register\n  append(breakToHost, LoadReg(temp, Offset(temp)).genReloc(*patch.llvmcpu));\n  // Jump to the epilogue to break to the host\n  append(breakToHost, JmpEpilogue().genReloc(*patch.llvmcpu));\n\n  // add target when callback return CONTINUE\n  append(breakToHost, TargetPrologue().genReloc(patch));\n\n  return breakToHost;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/Layer2_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"MCTargetDesc/X86BaseInfo.h\"\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/X86_64/Layer2_X86_64.h\"\n#include \"Patch/X86_64/RelocatableInst_X86_64.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Config.h\"\n\nnamespace QBDI {\n\nllvm::MCInst mov32rr(RegLLVM dst, RegLLVM src) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV32rr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst mov32ri(RegLLVM reg, rword imm) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV32ri);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n\n  return inst;\n}\n\nllvm::MCInst mov32mr(RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg, RegLLVM src) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV32mr);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst mov32rm8(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                      rword displacement, RegLLVM seg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOVZX32rm8);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst mov32rm16(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                       rword displacement, RegLLVM seg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOVZX32rm16);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst mov32rm(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV32rm);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst movzx32rr8(RegLLVM dst, RegLLVM src) {\n\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOVZX32rr8);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst mov64rr(RegLLVM dst, RegLLVM src) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV64rr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst mov64ri(RegLLVM reg, rword imm) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV64ri);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n\n  return inst;\n}\n\nllvm::MCInst mov64ri32(RegLLVM reg, rword imm) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV64ri32);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n\n  return inst;\n}\n\nllvm::MCInst mov64mr(RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg, RegLLVM src) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV64mr);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst mov64rm(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOV64rm);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst movzx64rr8(RegLLVM dst, RegLLVM src) {\n\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::MOVZX64rr8);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst test32ri(RegLLVM base, uint32_t imm) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::TEST32ri);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n\n  return inst;\n}\n\nllvm::MCInst test64ri32(RegLLVM base, uint32_t imm) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::TEST64ri32);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(imm));\n\n  return inst;\n}\n\nllvm::MCInst jmp32m(RegLLVM base, rword offset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::JMP32m);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n\n  return inst;\n}\n\nllvm::MCInst jmp64m(RegLLVM base, rword offset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::JMP64m);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n\n  return inst;\n}\n\nllvm::MCInst je(int32_t offset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::JCC_4);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::X86::CondCode::COND_E));\n\n  return inst;\n}\n\nllvm::MCInst jne(int32_t offset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::JCC_4);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createImm(llvm::X86::CondCode::COND_NE));\n\n  return inst;\n}\n\nllvm::MCInst jmp(rword offset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::JMP_4);\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n\n  return inst;\n}\n\nllvm::MCInst fxsave(RegLLVM base, rword offset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::FXSAVE);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n\n  return inst;\n}\n\nllvm::MCInst fxrstor(RegLLVM base, rword offset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::FXRSTOR);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n\n  return inst;\n}\n\nllvm::MCInst vextractf128(RegLLVM base, rword offset, RegLLVM src,\n                          uint8_t regoffset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::VEXTRACTF128mr);\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(regoffset));\n\n  return inst;\n}\n\nllvm::MCInst vinsertf128(RegLLVM dst, RegLLVM base, rword offset,\n                         uint8_t regoffset) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::VINSERTF128rm);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(offset));\n  inst.addOperand(llvm::MCOperand::createReg(0));\n  inst.addOperand(llvm::MCOperand::createImm(1));\n\n  return inst;\n}\n\nllvm::MCInst push32r(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::PUSH32r);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst push64r(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::PUSH64r);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst pop32r(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::POP32r);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst pop64r(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::POP64r);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst addr32i(RegLLVM dst, RegLLVM src, rword imm) {\n\n  // We use LEA to avoid flags to be modified\n  return lea32(dst, src, 1, 0, imm, 0);\n}\n\nllvm::MCInst addr64i(RegLLVM dst, RegLLVM src, rword imm) {\n\n  // We use LEA to avoid flags to be modified\n  return lea64(dst, src, 1, 0, imm, 0);\n}\n\nllvm::MCInst lea32(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                   rword displacement, RegLLVM seg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::LEA32r);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst lea64(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                   rword displacement, RegLLVM seg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::LEA64r);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(base.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(scale));\n  inst.addOperand(llvm::MCOperand::createReg(offset.getValue()));\n  inst.addOperand(llvm::MCOperand::createImm(displacement));\n  inst.addOperand(llvm::MCOperand::createReg(seg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst popf32() {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::POPF32);\n\n  return inst;\n}\n\nllvm::MCInst popf64() {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::POPF64);\n\n  return inst;\n}\n\nllvm::MCInst pushf32() {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::PUSHF32);\n\n  return inst;\n}\n\nllvm::MCInst pushf64() {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::PUSHF64);\n\n  return inst;\n}\n\nllvm::MCInst ret() {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::RET64);\n\n  return inst;\n}\n\nllvm::MCInst rdfsbase64(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::RDFSBASE64);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst rdgsbase64(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::RDGSBASE64);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst wrfsbase64(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::WRFSBASE64);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst wrgsbase64(RegLLVM reg) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::WRGSBASE64);\n  inst.addOperand(llvm::MCOperand::createReg(reg.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst nop() {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::NOOP);\n\n  return inst;\n}\n\nllvm::MCInst xor32rr(RegLLVM dst, RegLLVM src) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::XOR32rr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\nllvm::MCInst xor64rr(RegLLVM dst, RegLLVM src) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::XOR64rr);\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(dst.getValue()));\n  inst.addOperand(llvm::MCOperand::createReg(src.getValue()));\n\n  return inst;\n}\n\n// high level layer 2\n\n[[maybe_unused]] static bool isr8_15Reg(RegLLVM r) {\n  switch (r.getValue()) {\n    default:\n      return false;\n    case llvm::X86::R8:\n    case llvm::X86::R9:\n    case llvm::X86::R10:\n    case llvm::X86::R11:\n    case llvm::X86::R12:\n    case llvm::X86::R13:\n    case llvm::X86::R14:\n    case llvm::X86::R15:\n      return true;\n  }\n}\n\nstatic unsigned lenInstLEAtype(RegLLVM base, RegLLVM scale, Constant cst,\n                               RegLLVM segment) {\n\n  sword imm = cst;\n\n  if (segment != 0) {\n    return lenInstLEAtype(base, scale, cst, 0) + 1;\n  } else if (base == 0 and scale == 0) {\n    return (is_x86_64) ? 8 : 6;\n  } else if (base == llvm::X86::RIP) {\n    return 7;\n  } else if (base == 0) {\n    return (is_x86_64) ? 8 : 7;\n  } else {\n    unsigned len = (is_x86_64) ? 2 : 1;\n    if (scale != 0 or base == llvm::X86::ESP or base == llvm::X86::RSP or\n        base == llvm::X86::R12) {\n      len++;\n    }\n    if ((imm < -128) or (128 <= imm)) {\n      len = len + 5;\n    } else if (imm != 0 or base == llvm::X86::EBP or base == llvm::X86::RBP or\n               base == llvm::X86::R13) {\n      len = len + 2;\n    } else {\n      len++;\n    }\n    return len;\n  }\n}\n\nstatic inline RelocatableInst::UniquePtr\nDataBlockRelx86(llvm::MCInst &&inst, unsigned int opn, rword offset,\n                unsigned size64, unsigned size32) {\n  QBDI_REQUIRE_ABORT(opn < inst.getNumOperands(), \"Invalid offset {}\", opn);\n  QBDI_REQUIRE_ABORT(inst.getOperand(opn).isReg(), \"Invalid operand type\");\n  if constexpr (is_x86_64) {\n    inst.getOperand(opn /* AddrBaseReg */).setReg(GPR_ID[REG_PC].getValue());\n    return DataBlockRel::unique(std::forward<llvm::MCInst>(inst),\n                                opn + 3 /* AddrDisp */, offset - size64,\n                                size64);\n  } else {\n    inst.getOperand(opn /* AddrBaseReg */).setReg(0);\n    return DataBlockAbsRel::unique(std::forward<llvm::MCInst>(inst),\n                                   opn + 3 /* AddrDisp */, offset, size32);\n  }\n}\n\nRelocatableInst::UniquePtr JmpM(Offset offset) {\n  if constexpr (is_x86_64)\n    return DataBlockRel::unique(jmp64m(Reg(REG_PC), 0), 3, offset - 6, 6);\n  else\n    return DataBlockAbsRel::unique(jmp32m(0, 0), 3, offset, 6);\n}\n\nRelocatableInst::UniquePtr Fxsave(Offset offset) {\n  return DataBlockRelx86(fxsave(0, 0), 0, offset, 7, 7);\n}\n\nRelocatableInst::UniquePtr Fxrstor(Offset offset) {\n  return DataBlockRelx86(fxrstor(0, 0), 0, offset, 7, 7);\n}\n\nRelocatableInst::UniquePtr Vextractf128(Offset offset, RegLLVM src,\n                                        Constant regoffset) {\n  return DataBlockRelx86(vextractf128(0, 0, src, regoffset), 0, offset, 10, 10);\n}\n\nRelocatableInst::UniquePtr Vinsertf128(RegLLVM dst, Offset offset,\n                                       Constant regoffset) {\n  return DataBlockRelx86(vinsertf128(dst, 0, 0, regoffset), 2, offset, 10, 10);\n}\n\nRelocatableInst::UniquePtr Pushr(Reg reg) {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(push64r(reg), isr8_15Reg(reg) ? 2 : 1);\n  else\n    return NoRelocSized::unique(push32r(reg), 1);\n}\n\nRelocatableInst::UniquePtr Popr(Reg reg) {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(pop64r(reg), isr8_15Reg(reg) ? 2 : 1);\n  else\n    return NoRelocSized::unique(pop32r(reg), 1);\n}\n\nRelocatableInst::UniquePtr Add(Reg dest, Reg src, Constant cst) {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(addr64i(dest, src, cst),\n                                lenInstLEAtype(src, 0, cst, 0));\n  else\n    return NoRelocSized::unique(addr32i(dest, src, cst),\n                                lenInstLEAtype(src, 0, cst, 0));\n}\n\nRelocatableInst::UniquePtr Pushf() {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(pushf64(), 1);\n  else\n    return NoRelocSized::unique(pushf32(), 1);\n}\n\nRelocatableInst::UniquePtr Popf() {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(popf64(), 1);\n  else\n    return NoRelocSized::unique(popf32(), 1);\n}\n\nRelocatableInst::UniquePtr Ret() { return NoRelocSized::unique(ret(), 1); }\n\nRelocatableInst::UniquePtr Test(Reg reg, uint32_t value) {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(test64ri32(reg, value), 7);\n  else\n    return NoRelocSized::unique(test32ri(reg, value), 6);\n}\n\nRelocatableInst::UniquePtr Je(int32_t offset) {\n  return NoRelocSized::unique(je(offset), 6);\n}\n\nRelocatableInst::UniquePtr Jne(int32_t offset) {\n  return NoRelocSized::unique(jne(offset), 6);\n}\n\nRelocatableInst::UniquePtr Rdfsbase(Reg reg) {\n  return NoRelocSized::unique(rdfsbase64(reg), 5);\n}\n\nRelocatableInst::UniquePtr Rdgsbase(Reg reg) {\n  return NoRelocSized::unique(rdgsbase64(reg), 5);\n}\n\nRelocatableInst::UniquePtr Wrfsbase(Reg reg) {\n  return NoRelocSized::unique(wrfsbase64(reg), 5);\n}\n\nRelocatableInst::UniquePtr Wrgsbase(Reg reg) {\n  return NoRelocSized::unique(wrgsbase64(reg), 5);\n}\n\nRelocatableInst::UniquePtr Xorrr(RegLLVM dst, RegLLVM src) {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(xor64rr(dst, src), 3);\n  else\n    return NoRelocSized::unique(xor32rr(dst, src), 2);\n}\n\nRelocatableInst::UniquePtr Lea(RegLLVM dst, RegLLVM base, rword scale,\n                               RegLLVM offset, rword disp, RegLLVM seg) {\n  if (base == 0 and scale == 1 and offset != 0) {\n    base = offset;\n    offset = 0;\n  }\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(lea64(dst, base, scale, offset, disp, seg),\n                                lenInstLEAtype(base, offset, disp, seg));\n  else\n    return NoRelocSized::unique(lea32(dst, base, scale, offset, disp, seg),\n                                lenInstLEAtype(base, offset, disp, seg));\n}\n\nRelocatableInst::UniquePtr MovzxrAL(Reg dst) {\n  if constexpr (is_x86_64)\n    return NoRelocSized::unique(movzx64rr8(dst, llvm::X86::AL), 4);\n  else\n    return NoRelocSized::unique(movzx32rr8(dst, llvm::X86::AL), 3);\n}\n\nRelocatableInst::UniquePtr Mov64rm(RegLLVM dst, RegLLVM addr, RegLLVM seg) {\n  return NoRelocSized::unique(mov64rm(dst, addr, 1, 0, 0, seg),\n                              lenInstLEAtype(addr, 0, 0, seg));\n}\n\nRelocatableInst::UniquePtr Mov32rm(RegLLVM dst, RegLLVM addr, RegLLVM seg) {\n  unsigned len = 2;\n  if (seg != 0) {\n    len++;\n  }\n  if constexpr (is_x86_64) {\n    if (isr8_15Reg(addr) or addr == llvm::X86::RSP or addr == llvm::X86::RBP) {\n      len++;\n    }\n  } else {\n    if (addr == llvm::X86::ESP or addr == llvm::X86::EBP) {\n      len++;\n    }\n  }\n  return NoRelocSized::unique(mov32rm(dst, addr, 1, 0, 0, seg), len);\n}\n\nRelocatableInst::UniquePtr Mov32rm16(RegLLVM dst, RegLLVM addr, RegLLVM seg) {\n  unsigned len = 3;\n  if (seg != 0) {\n    len++;\n  }\n  if constexpr (is_x86_64) {\n    if (isr8_15Reg(addr) or addr == llvm::X86::RSP or addr == llvm::X86::RBP) {\n      len++;\n    }\n  } else {\n    if (addr == llvm::X86::ESP or addr == llvm::X86::EBP) {\n      len++;\n    }\n  }\n  return NoRelocSized::unique(mov32rm16(dst, addr, 1, 0, 0, seg), len);\n}\n\nRelocatableInst::UniquePtr Mov32rm8(RegLLVM dst, RegLLVM addr, RegLLVM seg) {\n  unsigned len = 3;\n  if (seg != 0) {\n    len++;\n  }\n  if constexpr (is_x86_64) {\n    if (isr8_15Reg(addr) or addr == llvm::X86::RSP or addr == llvm::X86::RBP) {\n      len++;\n    }\n  } else {\n    if (addr == llvm::X86::ESP or addr == llvm::X86::EBP) {\n      len++;\n    }\n  }\n  return NoRelocSized::unique(mov32rm8(dst, addr, 1, 0, 0, seg), len);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/Layer2_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LAYER2_X86_64_H\n#define LAYER2_X86_64_H\n\n#include <memory>\n#include <stdint.h>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"QBDI/State.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nclass RelocatableInst;\n\n// low level layer 2\n\nllvm::MCInst mov32rr(RegLLVM dst, RegLLVM src);\n\nllvm::MCInst mov32ri(RegLLVM reg, rword imm);\n\nllvm::MCInst mov32mr(RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg, RegLLVM src);\n\nllvm::MCInst mov32rm8(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                      rword displacement, RegLLVM seg);\n\nllvm::MCInst mov32rm16(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                       rword displacement, RegLLVM seg);\n\nllvm::MCInst mov32rm(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg);\n\nllvm::MCInst movzx32rr8(RegLLVM dst, RegLLVM src);\n\nllvm::MCInst mov64rr(RegLLVM dst, RegLLVM src);\n\nllvm::MCInst mov64ri(RegLLVM reg, rword imm);\n\nllvm::MCInst mov64ri32(RegLLVM reg, rword imm);\n\nllvm::MCInst mov64mr(RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg, RegLLVM src);\n\nllvm::MCInst mov64rm(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                     rword displacement, RegLLVM seg);\n\nllvm::MCInst movzx64rr8(RegLLVM dst, RegLLVM src);\n\nllvm::MCInst test32ri(RegLLVM base, uint32_t imm);\n\nllvm::MCInst test64ri32(RegLLVM base, uint32_t imm);\n\nllvm::MCInst je(int32_t offset);\n\nllvm::MCInst jne(int32_t offset);\n\nllvm::MCInst jmp32m(RegLLVM base, rword offset);\n\nllvm::MCInst jmp64m(RegLLVM base, rword offset);\n\nllvm::MCInst jmp(rword offset);\n\nllvm::MCInst fxsave(RegLLVM base, rword offset);\n\nllvm::MCInst fxrstor(RegLLVM base, rword offset);\n\nllvm::MCInst vextractf128(RegLLVM base, rword offset, RegLLVM src,\n                          uint8_t regoffset);\n\nllvm::MCInst vinsertf128(RegLLVM dst, RegLLVM base, rword offset,\n                         uint8_t regoffset);\n\nllvm::MCInst push32r(RegLLVM reg);\n\nllvm::MCInst push64r(RegLLVM reg);\n\nllvm::MCInst pop32r(RegLLVM reg);\n\nllvm::MCInst pop64r(RegLLVM reg);\n\nllvm::MCInst addr32i(RegLLVM dst, RegLLVM src, rword imm);\n\nllvm::MCInst addr64i(RegLLVM dst, RegLLVM src, rword imm);\n\nllvm::MCInst lea32(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                   rword displacement, RegLLVM seg);\n\nllvm::MCInst lea64(RegLLVM dst, RegLLVM base, rword scale, RegLLVM offset,\n                   rword displacement, RegLLVM seg);\n\nllvm::MCInst pushf32();\n\nllvm::MCInst pushf64();\n\nllvm::MCInst popf32();\n\nllvm::MCInst popf64();\n\nllvm::MCInst ret();\n\nllvm::MCInst rdfsbase(RegLLVM reg);\n\nllvm::MCInst rdgsbase(RegLLVM reg);\n\nllvm::MCInst wrfsbase(RegLLVM reg);\n\nllvm::MCInst wrgsbase(RegLLVM reg);\n\nllvm::MCInst nop();\n\nllvm::MCInst xor32rr(RegLLVM dst, RegLLVM src);\n\nllvm::MCInst xor64rr(RegLLVM dst, RegLLVM src);\n\n// high level layer 2\n\nstd::unique_ptr<RelocatableInst> JmpM(Offset offset);\n\nstd::unique_ptr<RelocatableInst> Fxsave(Offset offset);\n\nstd::unique_ptr<RelocatableInst> Fxrstor(Offset offset);\n\nstd::unique_ptr<RelocatableInst> Vextractf128(Offset offset, RegLLVM src,\n                                              Constant regoffset);\n\nstd::unique_ptr<RelocatableInst> Vinsertf128(RegLLVM dst, Offset offset,\n                                             Constant regoffset);\n\nstd::unique_ptr<RelocatableInst> Pushr(Reg reg);\n\nstd::unique_ptr<RelocatableInst> Popr(Reg reg);\n\nstd::unique_ptr<RelocatableInst> Add(Reg dest, Reg src, Constant cst);\n\nstd::unique_ptr<RelocatableInst> Pushf();\n\nstd::unique_ptr<RelocatableInst> Popf();\n\nstd::unique_ptr<RelocatableInst> Ret();\n\nstd::unique_ptr<RelocatableInst> Test(Reg reg, uint32_t value);\n\nstd::unique_ptr<RelocatableInst> Je(int32_t offset);\n\nstd::unique_ptr<RelocatableInst> Jne(int32_t offset);\n\nstd::unique_ptr<RelocatableInst> Rdfsbase(Reg reg);\n\nstd::unique_ptr<RelocatableInst> Rdgsbase(Reg reg);\n\nstd::unique_ptr<RelocatableInst> Wrfsbase(Reg reg);\n\nstd::unique_ptr<RelocatableInst> Wrgsbase(Reg reg);\n\nstd::unique_ptr<RelocatableInst> Xorrr(RegLLVM dst, RegLLVM src);\n\nstd::unique_ptr<RelocatableInst> Lea(RegLLVM dst, RegLLVM base, rword scale,\n                                     RegLLVM offset, rword disp, RegLLVM seg);\n\nstd::unique_ptr<RelocatableInst> MovzxrAL(Reg dst);\n\nstd::unique_ptr<RelocatableInst> Mov64rm(RegLLVM dst, RegLLVM addr,\n                                         RegLLVM seg);\n\nstd::unique_ptr<RelocatableInst> Mov32rm(RegLLVM dst, RegLLVM addr,\n                                         RegLLVM seg);\n\nstd::unique_ptr<RelocatableInst> Mov32rm16(RegLLVM dst, RegLLVM addr,\n                                           RegLLVM seg);\n\nstd::unique_ptr<RelocatableInst> Mov32rm8(RegLLVM dst, RegLLVM addr,\n                                          RegLLVM seg);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/X86_64/MemoryAccess_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <memory>\n#include <stddef.h>\n#include <stdint.h>\n#include <utility>\n#include <vector>\n\n#include \"llvm/ADT/ArrayRef.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/InstrRule.h\"\n#include \"Patch/MemoryAccess.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n#include \"Patch/X86_64/InstInfo_X86_64.h\"\n#include \"Patch/X86_64/PatchGenerator_X86_64.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Callback.h\"\n#include \"QBDI/State.h\"\n\nnamespace llvm {\nclass MCInstrDesc;\n}\n\nnamespace QBDI {\n\nenum MemoryTag : uint16_t {\n  MEM_READ_ADDRESS_TAG = MEMORY_TAG_BEGIN + 0,\n  MEM_WRITE_ADDRESS_TAG = MEMORY_TAG_BEGIN + 1,\n\n  MEM_READ_VALUE_TAG = MEMORY_TAG_BEGIN + 2,\n  MEM_WRITE_VALUE_TAG = MEMORY_TAG_BEGIN + 3,\n\n  MEM_READ_0_BEGIN_ADDRESS_TAG = MEMORY_TAG_BEGIN + 4,\n  MEM_READ_1_BEGIN_ADDRESS_TAG = MEMORY_TAG_BEGIN + 5,\n  MEM_WRITE_BEGIN_ADDRESS_TAG = MEMORY_TAG_BEGIN + 6,\n\n  MEM_READ_0_END_ADDRESS_TAG = MEMORY_TAG_BEGIN + 7,\n  MEM_READ_1_END_ADDRESS_TAG = MEMORY_TAG_BEGIN + 8,\n  MEM_WRITE_END_ADDRESS_TAG = MEMORY_TAG_BEGIN + 9,\n};\n\nvoid analyseMemoryAccessAddrValue(const ExecBlock &curExecBlock,\n                                  llvm::ArrayRef<ShadowInfo> &shadows,\n                                  std::vector<MemoryAccess> &dest,\n                                  const LLVMCPU &llvmcpu) {\n  if (shadows.size() < 1) {\n    return;\n  }\n\n  auto access = MemoryAccess();\n  access.flags = MEMORY_NO_FLAGS;\n\n  uint16_t expectValueTag;\n  const llvm::MCInst &inst = curExecBlock.getOriginalMCInst(shadows[0].instID);\n  switch (shadows[0].tag) {\n    default:\n      return;\n    case MEM_READ_ADDRESS_TAG:\n      access.type = MEMORY_READ;\n      access.size = getReadSize(inst, llvmcpu);\n      expectValueTag = MEM_READ_VALUE_TAG;\n      if (isMinSizeRead(inst)) {\n        access.flags |= MEMORY_MINIMUM_SIZE;\n      }\n      break;\n    case MEM_WRITE_ADDRESS_TAG:\n      access.type = MEMORY_WRITE;\n      access.size = getWriteSize(inst, llvmcpu);\n      expectValueTag = MEM_WRITE_VALUE_TAG;\n      if (isMinSizeRead(inst)) {\n        access.flags |= MEMORY_MINIMUM_SIZE;\n      }\n      break;\n  }\n\n  access.accessAddress = curExecBlock.getShadow(shadows[0].shadowID);\n  access.instAddress = curExecBlock.getInstAddress(shadows[0].instID);\n\n  if (access.size > sizeof(rword) or\n      llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    access.flags |= MEMORY_UNKNOWN_VALUE;\n    access.value = 0;\n    dest.push_back(std::move(access));\n    return;\n  }\n\n  size_t index = 0;\n  // search the index of MEM_x_VALUE_TAG. For most instruction, it's the next\n  // shadow.\n  do {\n    index += 1;\n    if (index >= shadows.size()) {\n      QBDI_ERROR(\"Not found shadow tag {:x} for instruction {:x}\",\n                 expectValueTag, access.instAddress);\n      return;\n    }\n    QBDI_REQUIRE_ACTION(shadows[0].instID == shadows[index].instID, return);\n  } while (shadows[index].tag != expectValueTag);\n\n  access.value = curExecBlock.getShadow(shadows[index].shadowID);\n\n  dest.push_back(std::move(access));\n}\n\nvoid analyseMemoryAccessAddrRange(const ExecBlock &curExecBlock,\n                                  llvm::ArrayRef<ShadowInfo> &shadows,\n                                  bool postInst,\n                                  std::vector<MemoryAccess> &dest,\n                                  const LLVMCPU &llvmcpu) {\n  if (shadows.size() < 1) {\n    return;\n  }\n  auto access = MemoryAccess();\n  access.flags = MEMORY_NO_FLAGS;\n\n  uint16_t expectValueTag;\n  unsigned accessAtomicSize;\n  switch (shadows[0].tag) {\n    default:\n      return;\n    case MEM_READ_0_BEGIN_ADDRESS_TAG:\n      access.type = MEMORY_READ;\n      expectValueTag = MEM_READ_0_END_ADDRESS_TAG;\n      accessAtomicSize = getReadSize(\n          curExecBlock.getOriginalMCInst(shadows[0].instID), llvmcpu);\n      break;\n    case MEM_READ_1_BEGIN_ADDRESS_TAG:\n      access.type = MEMORY_READ;\n      expectValueTag = MEM_READ_1_END_ADDRESS_TAG;\n      accessAtomicSize = getReadSize(\n          curExecBlock.getOriginalMCInst(shadows[0].instID), llvmcpu);\n      break;\n    case MEM_WRITE_BEGIN_ADDRESS_TAG:\n      access.type = MEMORY_WRITE;\n      expectValueTag = MEM_WRITE_END_ADDRESS_TAG;\n      accessAtomicSize = getWriteSize(\n          curExecBlock.getOriginalMCInst(shadows[0].instID), llvmcpu);\n      break;\n  }\n\n  access.instAddress = curExecBlock.getInstAddress(shadows[0].instID);\n  access.flags |= MEMORY_UNKNOWN_VALUE;\n  access.value = 0;\n\n  if (not postInst) {\n    access.accessAddress = curExecBlock.getShadow(shadows[0].shadowID);\n    access.flags |= MEMORY_UNKNOWN_SIZE;\n    access.size = 0;\n    dest.push_back(std::move(access));\n    return;\n  }\n\n  size_t index = 0;\n  // search the index of MEM_x_VALUE_TAG. For most instruction, it's the next\n  // shadow.\n  do {\n    index += 1;\n    if (index >= shadows.size()) {\n      QBDI_ERROR(\"Not found shadow tag {:x} for instruction {:x}\",\n                 expectValueTag, access.instAddress);\n      return;\n    }\n    QBDI_REQUIRE_ACTION(shadows[0].instID == shadows[index].instID, return);\n  } while (shadows[index].tag != expectValueTag);\n\n  rword beginAddress = curExecBlock.getShadow(shadows[0].shadowID);\n  rword endAddress = curExecBlock.getShadow(shadows[index].shadowID);\n\n  if (endAddress >= beginAddress) {\n    access.accessAddress = beginAddress;\n    access.size = endAddress - beginAddress;\n  } else {\n    // the endAddress is lesser than the begin address, this may be the case\n    // in X86 with REP prefix and DF=1\n    // In this case, the memory have been access between [endAddress +\n    // accessSize, beginAddress + accessAtomicSize)\n    access.accessAddress = endAddress + accessAtomicSize;\n    access.size = beginAddress - endAddress;\n  }\n\n  dest.push_back(std::move(access));\n}\n\nvoid analyseMemoryAccess(const ExecBlock &curExecBlock, uint16_t instID,\n                         bool afterInst, std::vector<MemoryAccess> &dest) {\n\n  llvm::ArrayRef<ShadowInfo> shadows = curExecBlock.getShadowByInst(instID);\n  const LLVMCPU &llvmcpu = curExecBlock.getLLVMCPUByInst(instID);\n  QBDI_DEBUG(\"Got {} shadows for Instruction {:x}\", shadows.size(), instID);\n\n  while (not shadows.empty()) {\n    QBDI_REQUIRE(shadows[0].instID == instID);\n\n    switch (shadows[0].tag) {\n      default:\n        break;\n      case MEM_READ_ADDRESS_TAG:\n        analyseMemoryAccessAddrValue(curExecBlock, shadows, dest, llvmcpu);\n        break;\n      case MEM_WRITE_ADDRESS_TAG:\n        if (afterInst) {\n          analyseMemoryAccessAddrValue(curExecBlock, shadows, dest, llvmcpu);\n        }\n        break;\n      case MEM_READ_0_BEGIN_ADDRESS_TAG:\n      case MEM_READ_1_BEGIN_ADDRESS_TAG:\n        analyseMemoryAccessAddrRange(curExecBlock, shadows, afterInst, dest,\n                                     llvmcpu);\n        break;\n      case MEM_WRITE_BEGIN_ADDRESS_TAG:\n        if (afterInst) {\n          analyseMemoryAccessAddrRange(curExecBlock, shadows, afterInst, dest,\n                                       llvmcpu);\n        }\n        break;\n    }\n    shadows = shadows.drop_front();\n  }\n}\n\nstatic const PatchGenerator::UniquePtrVec &\ngeneratePreReadInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  // REP prefix\n  if (hasREPPrefix(patch.metadata.inst)) {\n    if (isDoubleRead(patch.metadata.inst)) {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0), 0),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_0_BEGIN_ADDRESS_TAG)),\n          GetReadAddress::unique(Temp(0), 1),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_1_BEGIN_ADDRESS_TAG)));\n      return r;\n    } else {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_0_BEGIN_ADDRESS_TAG)));\n      return r;\n    }\n  }\n  // instruction with double read\n  else if (isDoubleRead(patch.metadata.inst)) {\n    if (getReadSize(patch.metadata.inst, llvmcpu) > sizeof(rword) or\n        llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0), 0),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadAddress::unique(Temp(0), 1),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)));\n      return r;\n    } else {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0), 0),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValue::unique(Temp(0), Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_VALUE_TAG)),\n          GetReadAddress::unique(Temp(0), 1),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValue::unique(Temp(0), Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_VALUE_TAG)));\n      return r;\n    }\n  } else {\n    if (getReadSize(patch.metadata.inst, llvmcpu) > sizeof(rword) or\n        llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)));\n      return r;\n    } else {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_ADDRESS_TAG)),\n          GetReadValue::unique(Temp(0), Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_VALUE_TAG)));\n      return r;\n    }\n  }\n}\n\nstatic const PatchGenerator::UniquePtrVec &\ngeneratePostReadInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  // REP prefix\n  if (hasREPPrefix(patch.metadata.inst)) {\n    if (isDoubleRead(patch.metadata.inst)) {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0), 0),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_0_END_ADDRESS_TAG)),\n          GetReadAddress::unique(Temp(0), 1),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_1_END_ADDRESS_TAG)));\n      return r;\n    } else {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetReadAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_READ_0_END_ADDRESS_TAG)));\n      return r;\n    }\n  } else {\n    static const PatchGenerator::UniquePtrVec r;\n    return r;\n  }\n}\n\nstatic const PatchGenerator::UniquePtrVec &\ngeneratePreWriteInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  const llvm::MCInstrDesc &desc =\n      llvmcpu.getMCII().get(patch.metadata.inst.getOpcode());\n\n  if (hasREPPrefix(patch.metadata.inst)) {\n    static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n        GetWriteAddress::unique(Temp(0)),\n        WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_BEGIN_ADDRESS_TAG)));\n    return r;\n  }\n  // Some instruction need to have the address get before the instruction\n  else if (mayChangeWriteAddr(patch.metadata.inst, desc) and\n           not isStackWrite(patch.metadata.inst)) {\n    static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n        GetWriteAddress::unique(Temp(0)),\n        WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)));\n    return r;\n  } else {\n    static const PatchGenerator::UniquePtrVec r;\n    return r;\n  }\n}\n\nstatic const PatchGenerator::UniquePtrVec &\ngeneratePostWriteInstrumentPatch(Patch &patch, const LLVMCPU &llvmcpu) {\n\n  const llvm::MCInstrDesc &desc =\n      llvmcpu.getMCII().get(patch.metadata.inst.getOpcode());\n\n  if (hasREPPrefix(patch.metadata.inst)) {\n    static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n        GetWriteAddress::unique(Temp(0)),\n        WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_END_ADDRESS_TAG)));\n    return r;\n  }\n  // Some instruction need to have the address get before the instruction\n  else if (mayChangeWriteAddr(patch.metadata.inst, desc) and\n           not isStackWrite(patch.metadata.inst)) {\n    if (getWriteSize(patch.metadata.inst, llvmcpu) > sizeof(rword) or\n        llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n      static const PatchGenerator::UniquePtrVec r;\n      return r;\n    } else {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          ReadTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWriteValue::unique(Temp(0), Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_VALUE_TAG)));\n      return r;\n    }\n  } else {\n    if (getWriteSize(patch.metadata.inst, llvmcpu) > sizeof(rword) or\n        llvmcpu.hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetWriteAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)));\n      return r;\n    } else {\n      static const PatchGenerator::UniquePtrVec r = conv_unique<PatchGenerator>(\n          GetWriteAddress::unique(Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_ADDRESS_TAG)),\n          GetWriteValue::unique(Temp(0), Temp(0)),\n          WriteTemp::unique(Temp(0), Shadow(MEM_WRITE_VALUE_TAG)));\n      return r;\n    }\n  }\n}\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessRead() {\n  return conv_unique<InstrRule>(\n      InstrRuleDynamic::unique(\n          DoesReadAccess::unique(), generatePreReadInstrumentPatch, PREINST,\n          false, PRIORITY_MEMACCESS_LIMIT + 1, RelocTagPreInstMemAccess),\n      InstrRuleDynamic::unique(\n          DoesReadAccess::unique(), generatePostReadInstrumentPatch, POSTINST,\n          false, PRIORITY_MEMACCESS_LIMIT + 1, RelocTagPostInstMemAccess));\n}\n\nstd::vector<std::unique_ptr<InstrRule>> getInstrRuleMemAccessWrite() {\n  return conv_unique<InstrRule>(\n      InstrRuleDynamic::unique(\n          DoesWriteAccess::unique(), generatePreWriteInstrumentPatch, PREINST,\n          false, PRIORITY_MEMACCESS_LIMIT, RelocTagPreInstMemAccess),\n      InstrRuleDynamic::unique(\n          DoesWriteAccess::unique(), generatePostWriteInstrumentPatch, POSTINST,\n          false, PRIORITY_MEMACCESS_LIMIT, RelocTagPostInstMemAccess));\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/PatchGenerator_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdint.h>\n#include <stdlib.h>\n#include <utility>\n\n#include \"MCTargetDesc/X86BaseInfo.h\"\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/Platform.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/X86_64/InstInfo_X86_64.h\"\n#include \"Patch/X86_64/Layer2_X86_64.h\"\n#include \"Patch/X86_64/PatchGenerator_X86_64.h\"\n#include \"Patch/X86_64/RelocatableInst_X86_64.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\n// Generic PatchGenerator that must be implemented by each target\n\n// TargetPrologue\n// ==============\n\nRelocatableInst::UniquePtrVec\nTargetPrologue::genReloc(const Patch &patch) const {\n  // no instruction for X86_64\n  return {};\n}\n\n// JmpEpilogue\n// ===========\n\nRelocatableInst::UniquePtrVec\nJmpEpilogue::genReloc(const LLVMCPU &llvmcpu) const {\n\n  return conv_unique<RelocatableInst>(EpilogueJump::unique());\n}\n\n// Target Specific PatchGenerator\n\n// GetPCOffset\n// ===========\n\nRelocatableInst::UniquePtrVec\nGetPCOffset::generate(const Patch &patch, TempManager &temp_manager) const {\n  if (type == ConstantType) {\n    return conv_unique<RelocatableInst>(\n        LoadImm::unique(temp_manager.getRegForTemp(temp),\n                        Constant(patch.metadata.endAddress() + cst)));\n  } else if (type == OperandType) {\n    QBDI_REQUIRE_ABORT(op < patch.metadata.inst.getNumOperands(),\n                       \"Invalid operand {} {}\", op, patch);\n\n    // FIXME: Implement for register operand\n    QBDI_REQUIRE_ABORT(patch.metadata.inst.getOperand(op).isImm(),\n                       \"Unexpected operand type {}\", patch);\n    return conv_unique<RelocatableInst>(\n        LoadImm::unique(temp_manager.getRegForTemp(temp),\n                        Constant(patch.metadata.endAddress() +\n                                 patch.metadata.inst.getOperand(op).getImm())));\n  }\n  _QBDI_UNREACHABLE();\n}\n\n// SimulateCall\n// ============\n\nRelocatableInst::UniquePtrVec\nSimulateCall::generate(const Patch &patch, TempManager &temp_manager) const {\n  RelocatableInst::UniquePtrVec p;\n\n  append(p, WriteTemp(temp, Offset(Reg(REG_PC))).generate(patch, temp_manager));\n  append(p, GetPCOffset(temp, Constant(0)).generate(patch, temp_manager));\n  p.push_back(Pushr(temp_manager.getRegForTemp(temp)));\n\n  return p;\n}\n\n// SimulateRet\n// ===========\n\nRelocatableInst::UniquePtrVec\nSimulateRet::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  RelocatableInst::UniquePtrVec p;\n\n  p.push_back(Popr(temp_manager.getRegForTemp(temp)));\n  if (inst.getNumOperands() == 1 and inst.getOperand(0).isImm()) {\n    p.push_back(\n        Add(Reg(REG_SP), Reg(REG_SP), Constant(inst.getOperand(0).getImm())));\n  }\n  append(p, WriteTemp(temp, Offset(Reg(REG_PC))).generate(patch, temp_manager));\n\n  return p;\n}\n\n// GetReadAddress\n// ==============\n\nRelocatableInst::UniquePtrVec\nGetReadAddress::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  // Check if this instruction does indeed read something\n  unsigned size = getReadSize(inst, *patch.llvmcpu);\n  if (size > 0) {\n    const llvm::MCInstrDesc &desc =\n        patch.llvmcpu->getMCII().get(inst.getOpcode());\n    uint64_t TSFlags = desc.TSFlags;\n    unsigned FormDesc = TSFlags & llvm::X86II::FormMask;\n    int memIndex = llvm::X86II::getMemoryOperandNo(TSFlags);\n\n    Reg dest = temp_manager.getRegForTemp(temp);\n    // If it is a stack read, return RSP value\n    if (isStackRead(inst)) {\n      if (inst.getOpcode() == llvm::X86::LEAVE ||\n          inst.getOpcode() == llvm::X86::LEAVE64) {\n        return conv_unique<RelocatableInst>(MovReg::unique(dest, Reg(REG_BP)));\n      } else {\n        return conv_unique<RelocatableInst>(MovReg::unique(dest, Reg(REG_SP)));\n      }\n    }\n    // Implicit RSI or RDI\n    else if (implicitDSIAccess(inst, desc)) {\n      QBDI_REQUIRE_ABORT(index < 2, \"Wrong index {} {}\", index, patch);\n      RegLLVM reg(0);\n      if (FormDesc == llvm::X86II::RawFrmSrc ||\n          (FormDesc == llvm::X86II::RawFrmDstSrc and index == 0)) {\n        // (R|E)SI\n        reg = Reg(4);\n        QBDI_REQUIRE(reg == llvm::X86::RSI || reg == llvm::X86::ESI);\n      } else {\n        // (R|E)DI\n        reg = Reg(5);\n        QBDI_REQUIRE(reg == llvm::X86::RDI || reg == llvm::X86::EDI);\n      }\n      return conv_unique<RelocatableInst>(MovReg::unique(dest, reg));\n    }\n    // Moffs access\n    else if (FormDesc == llvm::X86II::RawFrmMemOffs) {\n      QBDI_REQUIRE_ABORT(1 < inst.getNumOperands(),\n                         \"Unexpected number of operand {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(0).isImm(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      return conv_unique<RelocatableInst>(Lea(dest, 0, 1, 0,\n                                              inst.getOperand(0).getImm(),\n                                              inst.getOperand(1).getReg()));\n    }\n    // XLAT instruction\n    else if (inst.getOpcode() == llvm::X86::XLAT) {\n      // (R|E)BX\n      RegLLVM reg = Reg(1);\n      QBDI_REQUIRE(reg == llvm::X86::RBX || reg == llvm::X86::EBX);\n      return conv_unique<RelocatableInst>(MovzxrAL(dest),\n                                          Lea(dest, reg, 1, dest, 0, 0));\n    }\n    // Else replace the instruction with a LEA on the same address\n    else if (memIndex >= 0) {\n      unsigned realMemIndex = memIndex + llvm::X86II::getOperandBias(desc);\n\n      QBDI_REQUIRE_ABORT(realMemIndex + 4 < inst.getNumOperands(),\n                         \"Unexpected number of operand {} {}\", realMemIndex + 4,\n                         patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 0).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 1).isImm(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 2).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 3).isImm(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 4).isReg(),\n                         \"Unexpected operand type {}\", patch);\n\n      // If it uses PC as a base register, substitute PC\n      if (inst.getOperand(realMemIndex + 0).getReg() == GPR_ID[REG_PC]) {\n        return conv_unique<RelocatableInst>(\n            LoadImm::unique(temp_manager.getRegForTemp(0xFFFFFFFF),\n                            Constant(patch.metadata.endAddress())),\n            Lea(dest, temp_manager.getRegForTemp(0xFFFFFFFF),\n                inst.getOperand(realMemIndex + 1).getImm(),\n                inst.getOperand(realMemIndex + 2).getReg(),\n                inst.getOperand(realMemIndex + 3).getImm(),\n                inst.getOperand(realMemIndex + 4).getReg()));\n      } else {\n        return conv_unique<RelocatableInst>(\n            Lea(dest, inst.getOperand(realMemIndex + 0).getReg(),\n                inst.getOperand(realMemIndex + 1).getImm(),\n                inst.getOperand(realMemIndex + 2).getReg(),\n                inst.getOperand(realMemIndex + 3).getImm(),\n                inst.getOperand(realMemIndex + 4).getReg()));\n      }\n    }\n  }\n  QBDI_ABORT(\"Called on an instruction which does not make read access {}\",\n             patch);\n}\n\n// GetWriteAddress\n// ===============\n\nRelocatableInst::UniquePtrVec\nGetWriteAddress::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  // Check if this instruction does indeed read something\n  unsigned size = getWriteSize(inst, *patch.llvmcpu);\n  if (size > 0) {\n    const llvm::MCInstrDesc &desc =\n        patch.llvmcpu->getMCII().get(inst.getOpcode());\n    uint64_t TSFlags = desc.TSFlags;\n    unsigned FormDesc = TSFlags & llvm::X86II::FormMask;\n    int memIndex = llvm::X86II::getMemoryOperandNo(TSFlags);\n    unsigned opcode = inst.getOpcode();\n\n    Reg dest = temp_manager.getRegForTemp(temp);\n    // If it is a stack read, return RSP value\n    if (isStackWrite(inst)) {\n      if (inst.getOpcode() == llvm::X86::ENTER) {\n        return conv_unique<RelocatableInst>(MovReg::unique(dest, Reg(REG_BP)));\n      } else {\n        return conv_unique<RelocatableInst>(MovReg::unique(dest, Reg(REG_SP)));\n      }\n    }\n    // Implicit RSI or RDI\n    else if (implicitDSIAccess(inst, desc)) {\n      RegLLVM reg(0);\n      if (FormDesc == llvm::X86II::RawFrmSrc) {\n        // (R|E)SI\n        reg = Reg(4);\n        QBDI_REQUIRE(reg == llvm::X86::RSI || reg == llvm::X86::ESI);\n      } else {\n        // (R|E)DI\n        reg = Reg(5);\n        QBDI_REQUIRE(reg == llvm::X86::RDI || reg == llvm::X86::EDI);\n      }\n      return conv_unique<RelocatableInst>(MovReg::unique(dest, reg));\n    }\n    // Moffs access\n    else if (FormDesc == llvm::X86II::RawFrmMemOffs) {\n      QBDI_REQUIRE_ABORT(2 <= inst.getNumOperands(),\n                         \"Unexpected number of operand {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(0).isImm(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      return conv_unique<RelocatableInst>(Lea(dest, 0, 1, 0,\n                                              inst.getOperand(0).getImm(),\n                                              inst.getOperand(1).getReg()));\n    }\n    // MOVDIR64B instruction\n    else if (opcode == llvm::X86::MOVDIR64B16 ||\n             opcode == llvm::X86::MOVDIR64B32 ||\n             opcode == llvm::X86::MOVDIR64B64) {\n      QBDI_REQUIRE_ABORT(0 < inst.getNumOperands(),\n                         \"Unexpected number of operand {}\", patch);\n      return conv_unique<RelocatableInst>(\n          MovReg::unique(dest, inst.getOperand(0).getReg()));\n    }\n    // Else replace the instruction with a LEA on the same address\n    else if (memIndex >= 0) {\n      unsigned realMemIndex = memIndex + llvm::X86II::getOperandBias(desc);\n      QBDI_REQUIRE_ABORT(realMemIndex + 4 < inst.getNumOperands(),\n                         \"Unexpected number of operand {} {}\", realMemIndex + 4,\n                         patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 0).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 1).isImm(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 2).isReg(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 3).isImm(),\n                         \"Unexpected operand type {}\", patch);\n      QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 4).isReg(),\n                         \"Unexpected operand type {}\", patch);\n\n      // If it uses PC as a base register, substitute PC\n      if (inst.getOperand(realMemIndex + 0).getReg() == GPR_ID[REG_PC]) {\n        return conv_unique<RelocatableInst>(\n            LoadImm::unique(temp_manager.getRegForTemp(0xFFFFFFFF),\n                            Constant(patch.metadata.endAddress())),\n            Lea(dest, temp_manager.getRegForTemp(0xFFFFFFFF),\n                inst.getOperand(realMemIndex + 1).getImm(),\n                inst.getOperand(realMemIndex + 2).getReg(),\n                inst.getOperand(realMemIndex + 3).getImm(),\n                inst.getOperand(realMemIndex + 4).getReg()));\n      } else {\n        return conv_unique<RelocatableInst>(\n            Lea(dest, inst.getOperand(realMemIndex + 0).getReg(),\n                inst.getOperand(realMemIndex + 1).getImm(),\n                inst.getOperand(realMemIndex + 2).getReg(),\n                inst.getOperand(realMemIndex + 3).getImm(),\n                inst.getOperand(realMemIndex + 4).getReg()));\n      }\n    }\n  }\n  QBDI_ABORT(\"Called on an instruction which does not make write access {}\",\n             patch);\n}\n\n// GetReadValue\n// ============\n\nRelocatableInst::UniquePtrVec\nGetReadValue::generate(const Patch &patch, TempManager &temp_manager) const {\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const unsigned size = getReadSize(inst, *patch.llvmcpu);\n  QBDI_REQUIRE_ABORT(\n      size > 0, \"Called on an instruction which does not make read access {}\",\n      patch);\n\n  RegLLVM dst = temp_manager.getRegForTemp(temp);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    return conv_unique<RelocatableInst>(Xorrr(dst, dst));\n  } else if (is_bits_64 and size < sizeof(rword)) {\n    dst = temp_manager.getSizedSubReg(dst, 4);\n  } else if (size > sizeof(rword)) {\n    return conv_unique<RelocatableInst>(Xorrr(dst, dst));\n  }\n  Reg addr = temp_manager.getRegForTemp(address);\n  RegLLVM seg;\n\n  const llvm::MCInstrDesc &desc =\n      patch.llvmcpu->getMCII().get(inst.getOpcode());\n  uint64_t TSFlags = desc.TSFlags;\n  unsigned FormDesc = TSFlags & llvm::X86II::FormMask;\n\n  if (isStackRead(inst) or implicitDSIAccess(inst, desc) or\n      inst.getOpcode() == llvm::X86::XLAT) {\n    seg = 0;\n  } else if (FormDesc == llvm::X86II::RawFrmMemOffs) {\n    QBDI_REQUIRE_ABORT(1 < inst.getNumOperands(),\n                       \"Unexpected number of operand {}\", patch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(), \"Unexpected operand Type {}\",\n                       patch);\n    seg = inst.getOperand(1).getReg();\n  } else {\n    int memIndex = llvm::X86II::getMemoryOperandNo(TSFlags);\n    QBDI_REQUIRE_ABORT(memIndex >= 0, \"Fail to get memory access index {}\",\n                       patch);\n\n    unsigned realMemIndex = memIndex + llvm::X86II::getOperandBias(desc);\n    QBDI_REQUIRE_ABORT(inst.getNumOperands() > realMemIndex + 4,\n                       \"Invalid memory access index {} {}\", realMemIndex + 4,\n                       patch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 4).isReg(),\n                       \"Unexpected operand Type {}\", patch);\n    seg = inst.getOperand(realMemIndex + 4).getReg();\n  }\n\n  if (size == 8) {\n    return conv_unique<RelocatableInst>(Mov64rm(dst, addr, seg));\n  } else if (size == 4) {\n    return conv_unique<RelocatableInst>(Mov32rm(dst, addr, seg));\n  } else if (size == 2) {\n    return conv_unique<RelocatableInst>(Mov32rm16(dst, addr, seg));\n  } else if (size == 1) {\n    return conv_unique<RelocatableInst>(Mov32rm8(dst, addr, seg));\n  } else {\n    QBDI_ABORT(\"Unsupported read size {} {}\", size, patch);\n  }\n}\n\n// GetWriteValue\n// =============\n\nRelocatableInst::UniquePtrVec\nGetWriteValue::generate(const Patch &patch, TempManager &temp_manager) const {\n\n  const llvm::MCInst &inst = patch.metadata.inst;\n  const unsigned size = getWriteSize(inst, *patch.llvmcpu);\n  QBDI_REQUIRE_ABORT(\n      size > 0, \"Called on an instruction which does not make write access {}\",\n      patch);\n\n  RegLLVM dst = temp_manager.getRegForTemp(temp);\n\n  if (patch.llvmcpu->hasOptions(Options::OPT_DISABLE_MEMORYACCESS_VALUE)) {\n    return conv_unique<RelocatableInst>(Xorrr(dst, dst));\n  } else if (is_bits_64 and size < sizeof(rword)) {\n    dst = temp_manager.getSizedSubReg(dst, 4);\n  } else if (size > sizeof(rword)) {\n    return conv_unique<RelocatableInst>(Xorrr(dst, dst));\n  }\n  Reg addr = temp_manager.getRegForTemp(address);\n  unsigned seg = 0;\n\n  const llvm::MCInstrDesc &desc =\n      patch.llvmcpu->getMCII().get(inst.getOpcode());\n  uint64_t TSFlags = desc.TSFlags;\n  unsigned FormDesc = TSFlags & llvm::X86II::FormMask;\n\n  if (isStackWrite(inst) or implicitDSIAccess(inst, desc)) {\n    seg = 0;\n  } else if (FormDesc == llvm::X86II::RawFrmMemOffs) {\n    QBDI_REQUIRE_ABORT(1 < inst.getNumOperands(),\n                       \"Unexpected number of operand {}\", patch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(1).isReg(), \"Unexpected operand Type {}\",\n                       patch);\n    seg = inst.getOperand(1).getReg();\n  } else {\n    int memIndex = llvm::X86II::getMemoryOperandNo(TSFlags);\n    QBDI_REQUIRE_ABORT(memIndex >= 0, \"Fail to get memory access index {}\",\n                       patch);\n\n    unsigned realMemIndex = memIndex + llvm::X86II::getOperandBias(desc);\n    QBDI_REQUIRE_ABORT(inst.getNumOperands() > realMemIndex + 4,\n                       \"Invalid memory access index {} {}\", realMemIndex + 4,\n                       patch);\n    QBDI_REQUIRE_ABORT(inst.getOperand(realMemIndex + 4).isReg(),\n                       \"Unexpected operand Type {}\", patch);\n    seg = inst.getOperand(realMemIndex + 4).getReg();\n  }\n\n  if (size == 8) {\n    return conv_unique<RelocatableInst>(Mov64rm(dst, addr, seg));\n  } else if (size == 4) {\n    return conv_unique<RelocatableInst>(Mov32rm(dst, addr, seg));\n  } else if (size == 2) {\n    return conv_unique<RelocatableInst>(Mov32rm16(dst, addr, seg));\n  } else if (size == 1) {\n    return conv_unique<RelocatableInst>(Mov32rm8(dst, addr, seg));\n  } else {\n    QBDI_ABORT(\"Unsupported written size {} {}\", size, patch);\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/PatchGenerator_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHGENERATOR_X86_64_H\n#define PATCHGENERATOR_X86_64_H\n\n#include <memory>\n#include <stddef.h>\n#include <vector>\n\n#include \"QBDI/State.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass Patch;\nclass RelocatableInst;\nclass TempManager;\n\nclass GetPCOffset : public AutoClone<PatchGenerator, GetPCOffset> {\n\n  Temp temp;\n  Constant cst;\n  Operand op;\n  enum {\n    ConstantType,\n    OperandType,\n  } type;\n\npublic:\n  /*! Interpret a constant as a RIP relative offset and copy it in a temporary.\n   * It can be used to obtain the current value of RIP by using a constant of 0.\n   *\n   * @param[in] temp     A temporary where the value will be copied.\n   * @param[in] cst      The constant to be used.\n   */\n  GetPCOffset(Temp temp, Constant cst)\n      : temp(temp), cst(cst), op(0), type(ConstantType) {}\n\n  /*! Interpret an operand as a RIP relative offset and copy it in a temporary.\n   * It can be used to obtain jump/call targets or relative memory access\n   * addresses.\n   *\n   * @param[in] temp     A temporary where the value will be copied.\n   * @param[in] op       The  operand index (relative to the instruction\n   *                     LLVM MCInst representation) to be used.\n   */\n  GetPCOffset(Temp temp, Operand op)\n      : temp(temp), cst(0), op(op), type(OperandType) {}\n\n  /*! Output:\n   *\n   * If cst:\n   * MOV REG64 temp, IMM64 (cst + address + instSize)\n\n   * If op is an immediate:\n   * MOV REG64 temp, IMM64 (op + address + instSize)\n   *\n  */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass SimulateCall : public AutoClone<PatchGenerator, SimulateCall> {\n\n  Temp temp;\n\npublic:\n  /*! Simulate the effects of a call to the address stored in a temporary. The\n   * target address overwrites the stored value of RIP in the context part of\n   * the data block and the return address is pushed onto the stack. This\n   * generator signals a PC modification and triggers and end of basic block.\n   *\n   * @param[in] temp   Stores the call target address.\n   *                   Overwritten by this generator.\n   */\n  SimulateCall(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * MOV MEM64 DataBlock[Offset(RIP)], REG64 temp\n   * MOV REG64 temp, IMM64 (address + InstSize)\n   * PUSH REG64 temp\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  bool modifyPC() const override { return true; }\n};\n\nclass SimulateRet : public AutoClone<PatchGenerator, SimulateRet> {\n\n  Temp temp;\n\npublic:\n  /*! Simulate the effects of a return instruction. First the return address is\n   * popped from the stack into a temporary, then an optional stack deallocation\n   * is performed and finally the return address is written in the stored value\n   * of RIP in the context part of the data block. This generator signals a PC\n   * modification and triggers an end of basic block.\n   *\n   * The optional deallocation is performed if the current instruction has one\n   * single immediate operand (which is the case of RETIQ and RETIW).\n   *\n   * @param[in] temp   Any unused temporary, overwritten by this generator.\n   */\n  SimulateRet(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * POP REG64 temp\n   * ADD RSP, IMM64 deallocation # Optional\n   * MOV MEM64 DataBlock[Offset(RIP)], REG64 temp\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n\n  bool modifyPC() const override { return true; }\n};\n\nclass GetReadAddress : public AutoClone<PatchGenerator, GetReadAddress> {\n\n  Temp temp;\n  size_t index;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the address in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory address will be copied.\n   * @param[in] index     Index of access to saved when instruction\n   *                      does many read access\n   */\n  GetReadAddress(Temp temp, size_t index = 0) : temp(temp), index(index) {}\n\n  /*! Output:\n   *\n   * if stack access:\n   * MOV REG64 temp, REG64 RSP\n   *\n   * else:\n   * LEA REG64 temp, MEM64 addr\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetWriteAddress : public AutoClone<PatchGenerator, GetWriteAddress> {\n\n  Temp temp;\n\npublic:\n  /*! Resolve the memory address where the instructions will write its value and\n   * copy the address in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp   A temporary where the memory address will be copied.\n   */\n  GetWriteAddress(Temp temp) : temp(temp) {}\n\n  /*! Output:\n   *\n   * if stack access:\n   * MOV REG64 temp, REG64 RSP\n   *\n   * else:\n   * LEA REG64 temp, MEM64 addr\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetReadValue : public AutoClone<PatchGenerator, GetReadValue> {\n\n  Temp temp;\n  Temp address;\n\npublic:\n  /*! Resolve the memory address where the instructions will read its value and\n   * copy the value in a temporary. This PatchGenerator is only guaranteed to\n   * work before the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory value will be copied.\n   * @param[in] address   A temporary with the address to load. Unchanged\n   *                      (except if also temp)\n   */\n  GetReadValue(Temp temp, Temp address) : temp(temp), address(address) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\nclass GetWriteValue : public AutoClone<PatchGenerator, GetWriteValue> {\n\n  Temp temp;\n  Temp address;\n\npublic:\n  /*! Resolve the memory address where the instructions has written its value\n   * and copy back the value in a temporary. This PatchGenerator is only\n   * guaranteed to work after the instruction has been executed.\n   *\n   * @param[in] temp      A temporary where the memory value will be copied.\n   *                      The written address must be in the tmp.\n   * @param[in] address   A temporary with the address to load. Unchanged\n   *                      (except if also temp)\n   */\n  GetWriteValue(Temp temp, Temp address) : temp(temp), address(address) {}\n\n  /*! Output:\n   *\n   * MOV REG64 temp, MEM64 val\n   */\n  std::vector<std::unique_ptr<RelocatableInst>>\n  generate(const Patch &patch, TempManager &temp_manager) const override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/X86_64/PatchRuleAssembly_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <stddef.h>\n#include <vector>\n\n#include \"X86InstrInfo.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/State.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"ExecBlock/Context.h\"\n#include \"Patch/InstTransform.h\"\n#include \"Patch/PatchCondition.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/PatchRule.h\"\n#include \"Patch/PatchRuleAssembly.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n#include \"Patch/X86_64/ExecBlockFlags_X86_64.h\"\n#include \"Patch/X86_64/Layer2_X86_64.h\"\n#include \"Patch/X86_64/PatchGenerator_X86_64.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\nnamespace QBDI {\n\nnamespace {\n\nenum PatchGeneratorFlagsX86_64 {\n  MergeFlag = PatchGeneratorFlags::ArchSpecificFlags\n};\n\nstd::vector<PatchRule> getDefaultPatchRules(Options opts) {\n  std::vector<PatchRule> rules;\n\n  /* Rule #0: Avoid instrumenting instruction prefixes.\n   * Target:  X86 prefixes (LOCK, REP and other REX prefixes).\n   * Patch:   Output the unmodified MCInst but flag the patch as \"do not\n   * instrument\".\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::X86::LOCK_PREFIX),\n          OpIs::unique(llvm::X86::REX64_PREFIX),\n          OpIs::unique(llvm::X86::REP_PREFIX),\n          OpIs::unique(llvm::X86::REPNE_PREFIX),\n          OpIs::unique(llvm::X86::DATA16_PREFIX),\n          OpIs::unique(llvm::X86::CS_PREFIX),\n          OpIs::unique(llvm::X86::SS_PREFIX),\n          OpIs::unique(llvm::X86::DS_PREFIX),\n          OpIs::unique(llvm::X86::ES_PREFIX),\n          OpIs::unique(llvm::X86::FS_PREFIX),\n          OpIs::unique(llvm::X86::GS_PREFIX),\n          OpIs::unique(llvm::X86::XACQUIRE_PREFIX),\n          OpIs::unique(llvm::X86::XRELEASE_PREFIX))),\n      conv_unique<PatchGenerator>(\n          PatchGenFlags::unique(PatchGeneratorFlagsX86_64::MergeFlag),\n          ModifyInstruction::unique(InstTransform::UniquePtrVec())));\n\n  /* Rule #1: Simulate jmp to memory value using RIP addressing.\n   * Target:  JMP *[RIP + IMM]\n   * Patch:   Temp(0) := RIP + Constant(0)\n   *          JMP *[RIP + IMM] --> MOV Temp(1), [Temp(0) + IMM]\n   *          DataBlock[Offset(RIP)] := Temp(1)\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP64m),\n                                              UseReg::unique(Reg(REG_PC)))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)),\n              SetOpcode::unique(llvm::X86::MOV64rm),\n              AddOperand::unique(Operand(0), Temp(1)))),\n          WriteTemp::unique(Temp(1), Offset(Reg(REG_PC)))));\n\n  /* Rule #2: Simulate call to memory value using RIP addressing.\n   * Target:  CALL *[RIP + IMM]\n   * Patch:   Temp(0) := RIP + Constant(0)\n   *          CALL *[RIP + IMM] --> MOV Temp(1), [Temp(0) + IMM]\n   *          SimulateCall(Temp(1))\n   */\n  rules.emplace_back(\n      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL64m),\n                                              UseReg::unique(Reg(REG_PC)))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)),\n              SetOpcode::unique(llvm::X86::MOV64rm),\n              AddOperand::unique(Operand(0), Temp(1)))),\n          SimulateCall::unique(Temp(1))));\n\n  /* Rule #3: Generic RIP patching.\n   * Target:  Any instruction with RIP as operand, e.g. LEA RAX, [RIP + 1]\n   * Patch:   Temp(0) := rip\n   *          LEA RAX, [RIP + IMM] --> LEA RAX, [Temp(0) + IMM]\n   */\n  rules.emplace_back(\n      UseReg::unique(Reg(REG_PC)),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Constant(0)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0))))));\n\n  /* Rule #4: Simulate JMP to memory value.\n   * Target:  JMP *MEM\n   * Patch:   JMP *MEM --> MOV Temp(0), MEM\n   *          DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP32m),\n                                             OpIs::unique(llvm::X86::JMP64m))),\n      conv_unique<PatchGenerator>(\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOpcode::unique(is_x86 ? llvm::X86::MOV32rm\n                                       : llvm::X86::MOV64rm),\n              AddOperand::unique(Operand(0), Temp(0)))),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));\n\n  /* Rule #5: Simulate CALL to memory value.\n   * Target:  CALL MEM\n   * Patch:   CALL MEM --> MOV Temp(0), MEM\n   *          SimulateCall(Temp(1))\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL32m),\n                                             OpIs::unique(llvm::X86::CALL64m))),\n      conv_unique<PatchGenerator>(\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOpcode::unique(is_x86 ? llvm::X86::MOV32rm\n                                       : llvm::X86::MOV64rm),\n              AddOperand::unique(Operand(0), Temp(0)))),\n          SimulateCall::unique(Temp(0))));\n\n  /* Rule #6: Simulate JMP to constant value.\n   * Target:  JMP IMM\n   * Patch:   Temp(0) := RIP + Operand(0)\n   *          DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP_1),\n                                             OpIs::unique(llvm::X86::JMP_2),\n                                             OpIs::unique(llvm::X86::JMP_4))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(0)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));\n\n  /* Rule #7: Simulate JMP to register value.\n   * Target:  JMP REG\n   * Patch:   Temp(0) := Operand(0)\n   *          DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP32r),\n                                             OpIs::unique(llvm::X86::JMP64r))),\n      conv_unique<PatchGenerator>(\n          GetOperand::unique(Temp(0), Operand(0)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));\n\n  /* Rule #8: Simulate CALL to register value.\n   * Target:  CALL REG\n   * Patch:   Temp(0) := Operand(0)\n   *          SimulateCall(Temp(0))\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL32r),\n                                             OpIs::unique(llvm::X86::CALL64r))),\n      conv_unique<PatchGenerator>(GetOperand::unique(Temp(0), Operand(0)),\n                                  SimulateCall::unique(Temp(0))));\n\n  /* Rule #9: Simulate Jcc IMM8.\n   * Target:  Jcc IMM8\n   * Patch:     Temp(0) := RIP + Operand(0)\n   *         ---Jcc IMM8 --> Jcc END\n   *         |  Temp(0) := RIP + Constant(0)\n   *         -->END: DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::X86::JCC_1), OpIs::unique(llvm::X86::LOOP),\n          OpIs::unique(llvm::X86::LOOPE), OpIs::unique(llvm::X86::LOOPNE),\n          OpIs::unique(llvm::X86::JRCXZ), OpIs::unique(llvm::X86::JECXZ),\n          OpIs::unique(llvm::X86::JCXZ))),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(0)),\n          ModifyInstruction::unique(\n              conv_unique<InstTransform>(SetOperand::unique(\n                  Operand(0),\n                  Constant(is_x86 ? 6 : 11)) // Offset to jump the next load.\n                                         )),\n          GetPCOffset::unique(Temp(0), Constant(0)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));\n\n  /* Rule #10: Simulate Jcc IMM16.\n   * Target:  Jcc IMM16\n   * Patch:     Temp(0) := RIP + Operand(0)\n   *         ---Jcc IMM16 --> Jcc END\n   *         |  Temp(0) := RIP + Constant(0)\n   *         -->END: DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      OpIs::unique(llvm::X86::JCC_2),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(0)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(0), Constant(is_x86 ? 7 : 12)))),\n          GetPCOffset::unique(Temp(0), Constant(0)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));\n\n  /* Rule #11: Simulate Jcc IMM32.\n   * Target:  Jcc IMM32\n   * Patch:     Temp(0) := RIP + Operand(0)\n   *         ---Jcc IMM32 --> Jcc END\n   *         |  Temp(0) := RIP + Constant(0)\n   *         -->END: DataBlock[Offset(RIP)] := Temp(0)\n   */\n  rules.emplace_back(\n      OpIs::unique(llvm::X86::JCC_4),\n      conv_unique<PatchGenerator>(\n          GetPCOffset::unique(Temp(0), Operand(0)),\n          ModifyInstruction::unique(conv_unique<InstTransform>(\n              SetOperand::unique(Operand(0), Constant(is_x86 ? 9 : 14)))),\n          GetPCOffset::unique(Temp(0), Constant(0)),\n          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));\n\n  /* Rule #12: Simulate CALL to constant offset.\n   * Target:   CALL IMM\n   * Patch:    Temp(0) := RIP + Operand(0)\n   *           SimulateCall(Temp(0))\n   */\n  rules.emplace_back(\n      Or::unique(\n          conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL64pcrel32),\n                                      OpIs::unique(llvm::X86::CALLpcrel16),\n                                      OpIs::unique(llvm::X86::CALLpcrel32))),\n      conv_unique<PatchGenerator>(GetPCOffset::unique(Temp(0), Operand(0)),\n                                  SimulateCall::unique(Temp(0))));\n\n  /* Rule #13: Simulate return.\n   * Target:   RET\n   * Patch:    SimulateRet(Temp(0))\n   */\n  rules.emplace_back(\n      Or::unique(conv_unique<PatchCondition>(\n          OpIs::unique(llvm::X86::RET32), OpIs::unique(llvm::X86::RET64),\n          OpIs::unique(llvm::X86::RET16), OpIs::unique(llvm::X86::RETI32),\n          OpIs::unique(llvm::X86::RETI64), OpIs::unique(llvm::X86::RETI16))),\n      conv_unique<PatchGenerator>(SimulateRet::unique(Temp(0))));\n\n  /* Rule #14: Default rule for every other instructions.\n   * Target:   *\n   * Patch:    Output original unmodified instructions.\n   */\n  rules.emplace_back(True::unique(),\n                     conv_unique<PatchGenerator>(ModifyInstruction::unique(\n                         InstTransform::UniquePtrVec())));\n\n  return rules;\n}\n\n} // namespace\n\nPatchRuleAssembly::PatchRuleAssembly(Options opts)\n    : patchRules(getDefaultPatchRules(opts)), options(opts),\n      mergePending(false) {}\n\nPatchRuleAssembly::~PatchRuleAssembly() = default;\n\nvoid PatchRuleAssembly::reset() { mergePending = false; }\n\nbool PatchRuleAssembly::changeOptions(Options opts) {\n  // reset the current state. Options cannot be change during the Engine::patch\n  // method\n  reset();\n\n  const Options needRecreate = Options::OPT_DISABLE_FPR |\n#if defined(QBDI_ARCH_X86_64)\n                               Options::OPT_ENABLE_FS_GS |\n#endif\n                               Options::OPT_DISABLE_OPTIONAL_FPR |\n                               Options::OPT_DISABLE_MEMORYACCESS_VALUE;\n  if ((opts & needRecreate) != (options & needRecreate)) {\n    patchRules = getDefaultPatchRules(opts);\n    options = opts;\n    return true;\n  }\n  options = opts;\n  return false;\n}\n\nstatic void setRegisterSaved(Patch &patch) {\n\n  if constexpr (is_x86) {\n    switch (patch.metadata.inst.getOpcode()) {\n      case llvm::X86::PUSHA16:\n      case llvm::X86::PUSHA32:\n      case llvm::X86::POPA16:\n      case llvm::X86::POPA32:\n        // allows TmpManager to reuse the register\n        for (unsigned i = 0; i < AVAILABLE_GPR; i++) {\n          patch.regUsage[i] |= RegisterUsage::RegisterSaved;\n        }\n        break;\n      default:\n        break;\n    }\n  }\n\n  return;\n}\n\nbool PatchRuleAssembly::generate(const llvm::MCInst &inst, rword address,\n                                 uint32_t instSize, const LLVMCPU &llvmcpu,\n                                 std::vector<Patch> &patchList) {\n\n  Patch instPatch{inst, address, instSize, llvmcpu};\n  setRegisterSaved(instPatch);\n\n  for (uint32_t j = 0; j < patchRules.size(); j++) {\n    if (patchRules[j].canBeApplied(instPatch, llvmcpu)) {\n      QBDI_DEBUG(\"Patch rule {} applied\", j);\n      if (mergePending) {\n        QBDI_REQUIRE_ABORT(patchList.size() > 0, \"No previous patch to merge\");\n        QBDI_DEBUG(\"Previous instruction merged\");\n\n        // 1. generate the patch for the current instruction\n        patchRules[j].apply(instPatch, llvmcpu);\n\n        // 2. get insert position\n        int position = -1;\n        for (auto &p : instPatch.patchGenFlags) {\n          if (p.second == PatchGeneratorFlags::ModifyInstructionBeginFlags) {\n            position = p.first;\n            break;\n          }\n        }\n        QBDI_REQUIRE_ABORT(\n            position != -1,\n            \"Fail to get the position to insert the new patch {}\", instPatch);\n\n        // 3. add the instruction to merge at the flags ModifyInstructionFlags\n        Patch &mergePatch = patchList.back();\n        instPatch.insertAt(position, std::move(mergePatch.insts));\n\n        // 4. keep some metadata\n        instPatch.metadata.address = mergePatch.metadata.address;\n        instPatch.metadata.instSize += mergePatch.metadata.instSize;\n        instPatch.metadata.execblockFlags |= mergePatch.metadata.execblockFlags;\n        instPatch.metadata.prefix = mergePatch.metadata.prefix;\n        instPatch.metadata.prefix.push_back(mergePatch.metadata.inst);\n\n        // 5. replace the patch\n        mergePatch = std::move(instPatch);\n\n      } else {\n        patchRules[j].apply(instPatch, llvmcpu);\n        patchList.push_back(std::move(instPatch));\n      }\n      Patch &patch = patchList.back();\n      mergePending = false;\n      for (auto &p : patch.patchGenFlags) {\n        mergePending |= (p.second == PatchGeneratorFlagsX86_64::MergeFlag);\n      }\n\n      if (mergePending) {\n        return false;\n      } else if (patch.metadata.modifyPC) {\n        reset();\n        return true;\n      } else {\n        return false;\n      }\n    }\n  }\n  QBDI_ABORT(\"Not PatchRule found for: {}\", instPatch);\n}\n\nbool PatchRuleAssembly::earlyEnd(const LLVMCPU &llvmcpu,\n                                 std::vector<Patch> &patchList) {\n  if (mergePending) {\n    if (patchList.size() == 0) {\n      QBDI_CRITICAL(\"Cannot remove pending Patch\");\n      return false;\n    }\n    patchList.pop_back();\n  }\n  reset();\n  return true;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/PatchRuleAssembly_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCHRULEASSEMBLY_X86_64_H\n#define PATCHRULEASSEMBLY_X86_64_H\n\n#include <stdbool.h>\n\n#include \"Patch/PatchRuleAssemblyBase.h\"\n\nnamespace QBDI {\nclass PatchRule;\n\nclass PatchRuleAssembly final : public PatchRuleAssemblyBase {\n  std::vector<PatchRule> patchRules;\n  Options options;\n  bool mergePending;\n\n  void reset();\n\npublic:\n  PatchRuleAssembly(Options opts);\n\n  ~PatchRuleAssembly();\n\n  bool changeOptions(Options opts) override;\n\n  bool generate(const llvm::MCInst &inst, rword address, uint32_t instSize,\n                const LLVMCPU &llvmcpu, std::vector<Patch> &patchList) override;\n\n  bool earlyEnd(const LLVMCPU &llvmcpu, std::vector<Patch> &patchList) override;\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/X86_64/Register_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cstdint>\n#include <map>\n#include <stddef.h>\n\n#include \"X86InstrInfo.h\"\n\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nconst RegLLVM GPR_ID[] = {\n#if defined(QBDI_ARCH_X86_64)\n    llvm::X86::RAX, llvm::X86::RBX,    llvm::X86::RCX, llvm::X86::RDX,\n    llvm::X86::RSI, llvm::X86::RDI,    llvm::X86::R8,  llvm::X86::R9,\n    llvm::X86::R10, llvm::X86::R11,    llvm::X86::R12, llvm::X86::R13,\n    llvm::X86::R14, llvm::X86::R15,    llvm::X86::RBP, llvm::X86::RSP,\n    llvm::X86::RIP, llvm::X86::EFLAGS,\n#elif defined(QBDI_ARCH_X86)\n    llvm::X86::EAX, llvm::X86::EBX,    llvm::X86::ECX, llvm::X86::EDX,\n    llvm::X86::ESI, llvm::X86::EDI,    llvm::X86::EBP, llvm::X86::ESP,\n    llvm::X86::EIP, llvm::X86::EFLAGS,\n#else\n#error \"Wrong architecture\"\n#endif\n};\n\nconst RegLLVM FLAG_ID[] = {\n    llvm::X86::DF,\n};\n\nconst RegLLVM SEG_ID[] = {\n    llvm::X86::SS,  llvm::X86::CS,      llvm::X86::DS,  llvm::X86::ES,\n    llvm::X86::FS,  llvm::X86::FS_BASE, llvm::X86::GS,  llvm::X86::GS_BASE,\n    llvm::X86::SSP, llvm::X86::EIZ,     llvm::X86::RIZ,\n};\n\nconst std::map<RegLLVM, int16_t> FPR_ID = {\n    {llvm::X86::FPCW, offsetof(FPRState, rfcw)},\n    {llvm::X86::FPSW, offsetof(FPRState, rfsw)},\n    {llvm::X86::MXCSR, offsetof(FPRState, mxcsr)},\n    {llvm::X86::ST0, offsetof(FPRState, stmm0)},\n    {llvm::X86::ST1, offsetof(FPRState, stmm1)},\n    {llvm::X86::ST2, offsetof(FPRState, stmm2)},\n    {llvm::X86::ST3, offsetof(FPRState, stmm3)},\n    {llvm::X86::ST4, offsetof(FPRState, stmm4)},\n    {llvm::X86::ST5, offsetof(FPRState, stmm5)},\n    {llvm::X86::ST6, offsetof(FPRState, stmm6)},\n    {llvm::X86::ST7, offsetof(FPRState, stmm7)},\n    {llvm::X86::MM0, offsetof(FPRState, stmm0)},\n    {llvm::X86::MM1, offsetof(FPRState, stmm1)},\n    {llvm::X86::MM2, offsetof(FPRState, stmm2)},\n    {llvm::X86::MM3, offsetof(FPRState, stmm3)},\n    {llvm::X86::MM4, offsetof(FPRState, stmm4)},\n    {llvm::X86::MM5, offsetof(FPRState, stmm5)},\n    {llvm::X86::MM6, offsetof(FPRState, stmm6)},\n    {llvm::X86::MM7, offsetof(FPRState, stmm7)},\n    {llvm::X86::XMM0, offsetof(FPRState, xmm0)},\n    {llvm::X86::XMM1, offsetof(FPRState, xmm1)},\n    {llvm::X86::XMM2, offsetof(FPRState, xmm2)},\n    {llvm::X86::XMM3, offsetof(FPRState, xmm3)},\n    {llvm::X86::XMM4, offsetof(FPRState, xmm4)},\n    {llvm::X86::XMM5, offsetof(FPRState, xmm5)},\n    {llvm::X86::XMM6, offsetof(FPRState, xmm6)},\n    {llvm::X86::XMM7, offsetof(FPRState, xmm7)},\n#if defined(QBDI_ARCH_X86_64)\n    {llvm::X86::XMM8, offsetof(FPRState, xmm8)},\n    {llvm::X86::XMM9, offsetof(FPRState, xmm9)},\n    {llvm::X86::XMM10, offsetof(FPRState, xmm10)},\n    {llvm::X86::XMM11, offsetof(FPRState, xmm11)},\n    {llvm::X86::XMM12, offsetof(FPRState, xmm12)},\n    {llvm::X86::XMM13, offsetof(FPRState, xmm13)},\n    {llvm::X86::XMM14, offsetof(FPRState, xmm14)},\n    {llvm::X86::XMM15, offsetof(FPRState, xmm15)},\n#elif defined(QBDI_ARCH_X86)\n    {llvm::X86::XMM8, -1},\n    {llvm::X86::XMM9, -1},\n    {llvm::X86::XMM10, -1},\n    {llvm::X86::XMM11, -1},\n    {llvm::X86::XMM12, -1},\n    {llvm::X86::XMM13, -1},\n    {llvm::X86::XMM14, -1},\n    {llvm::X86::XMM15, -1},\n#endif\n    {llvm::X86::XMM16, -1},\n    {llvm::X86::XMM17, -1},\n    {llvm::X86::XMM18, -1},\n    {llvm::X86::XMM19, -1},\n    {llvm::X86::XMM20, -1},\n    {llvm::X86::XMM21, -1},\n    {llvm::X86::XMM22, -1},\n    {llvm::X86::XMM23, -1},\n    {llvm::X86::XMM24, -1},\n    {llvm::X86::XMM25, -1},\n    {llvm::X86::XMM26, -1},\n    {llvm::X86::XMM27, -1},\n    {llvm::X86::XMM28, -1},\n    {llvm::X86::XMM29, -1},\n    {llvm::X86::XMM30, -1},\n    {llvm::X86::XMM31, -1},\n    {llvm::X86::YMM0, offsetof(FPRState, ymm0)},\n    {llvm::X86::YMM1, offsetof(FPRState, ymm1)},\n    {llvm::X86::YMM2, offsetof(FPRState, ymm2)},\n    {llvm::X86::YMM3, offsetof(FPRState, ymm3)},\n    {llvm::X86::YMM4, offsetof(FPRState, ymm4)},\n    {llvm::X86::YMM5, offsetof(FPRState, ymm5)},\n    {llvm::X86::YMM6, offsetof(FPRState, ymm6)},\n    {llvm::X86::YMM7, offsetof(FPRState, ymm7)},\n#if defined(QBDI_ARCH_X86_64)\n    {llvm::X86::YMM8, offsetof(FPRState, ymm8)},\n    {llvm::X86::YMM9, offsetof(FPRState, ymm9)},\n    {llvm::X86::YMM10, offsetof(FPRState, ymm10)},\n    {llvm::X86::YMM11, offsetof(FPRState, ymm11)},\n    {llvm::X86::YMM12, offsetof(FPRState, ymm12)},\n    {llvm::X86::YMM13, offsetof(FPRState, ymm13)},\n    {llvm::X86::YMM14, offsetof(FPRState, ymm14)},\n    {llvm::X86::YMM15, offsetof(FPRState, ymm15)},\n#elif defined(QBDI_ARCH_X86)\n    {llvm::X86::YMM8, -1},\n    {llvm::X86::YMM9, -1},\n    {llvm::X86::YMM10, -1},\n    {llvm::X86::YMM11, -1},\n    {llvm::X86::YMM12, -1},\n    {llvm::X86::YMM13, -1},\n    {llvm::X86::YMM14, -1},\n    {llvm::X86::YMM15, -1},\n#endif\n    {llvm::X86::YMM16, -1},\n    {llvm::X86::YMM17, -1},\n    {llvm::X86::YMM18, -1},\n    {llvm::X86::YMM19, -1},\n    {llvm::X86::YMM20, -1},\n    {llvm::X86::YMM21, -1},\n    {llvm::X86::YMM22, -1},\n    {llvm::X86::YMM23, -1},\n    {llvm::X86::YMM24, -1},\n    {llvm::X86::YMM25, -1},\n    {llvm::X86::YMM26, -1},\n    {llvm::X86::YMM27, -1},\n    {llvm::X86::YMM28, -1},\n    {llvm::X86::YMM29, -1},\n    {llvm::X86::YMM30, -1},\n    {llvm::X86::YMM31, -1},\n    {llvm::X86::ZMM0, -1},\n    {llvm::X86::ZMM1, -1},\n    {llvm::X86::ZMM2, -1},\n    {llvm::X86::ZMM3, -1},\n    {llvm::X86::ZMM4, -1},\n    {llvm::X86::ZMM5, -1},\n    {llvm::X86::ZMM6, -1},\n    {llvm::X86::ZMM7, -1},\n    {llvm::X86::ZMM8, -1},\n    {llvm::X86::ZMM9, -1},\n    {llvm::X86::ZMM10, -1},\n    {llvm::X86::ZMM11, -1},\n    {llvm::X86::ZMM12, -1},\n    {llvm::X86::ZMM13, -1},\n    {llvm::X86::ZMM14, -1},\n    {llvm::X86::ZMM15, -1},\n    {llvm::X86::ZMM16, -1},\n    {llvm::X86::ZMM17, -1},\n    {llvm::X86::ZMM18, -1},\n    {llvm::X86::ZMM19, -1},\n    {llvm::X86::ZMM20, -1},\n    {llvm::X86::ZMM21, -1},\n    {llvm::X86::ZMM22, -1},\n    {llvm::X86::ZMM23, -1},\n    {llvm::X86::ZMM24, -1},\n    {llvm::X86::ZMM25, -1},\n    {llvm::X86::ZMM26, -1},\n    {llvm::X86::ZMM27, -1},\n    {llvm::X86::ZMM28, -1},\n    {llvm::X86::ZMM29, -1},\n    {llvm::X86::ZMM30, -1},\n    {llvm::X86::ZMM31, -1},\n};\n\nconst unsigned int size_GPR_ID = sizeof(GPR_ID) / sizeof(RegLLVM);\nconst unsigned int size_FLAG_ID = sizeof(FLAG_ID) / sizeof(RegLLVM);\nconst unsigned int size_SEG_ID = sizeof(SEG_ID) / sizeof(RegLLVM);\n\nnamespace {\n\nconstexpr uint16_t REGISTER_1BYTE[] = {\n    llvm::X86::AL,\n    llvm::X86::BL,\n    llvm::X86::CL,\n    llvm::X86::DL,\n    llvm::X86::AH,\n    llvm::X86::BH,\n    llvm::X86::CH,\n    llvm::X86::DH,\n    llvm::X86::SIL,\n    llvm::X86::DIL,\n    llvm::X86::SPL,\n    llvm::X86::BPL,\n    llvm::X86::R8B,\n    llvm::X86::R9B,\n    llvm::X86::R10B,\n    llvm::X86::R11B,\n    llvm::X86::R12B,\n    llvm::X86::R13B,\n    llvm::X86::R14B,\n    llvm::X86::R15B,\n    llvm::X86::R16B,\n    llvm::X86::R17B,\n    llvm::X86::R18B,\n    llvm::X86::R19B,\n    llvm::X86::R20B,\n    llvm::X86::R21B,\n    llvm::X86::R22B,\n    llvm::X86::R23B,\n    llvm::X86::R24B,\n    llvm::X86::R25B,\n    llvm::X86::R26B,\n    llvm::X86::R27B,\n    llvm::X86::R28B,\n    llvm::X86::R29B,\n    llvm::X86::R30B,\n    llvm::X86::R31B,\n    // artificial\n    llvm::X86::SIH,\n    llvm::X86::DIH,\n    llvm::X86::BPH,\n    llvm::X86::SPH,\n    llvm::X86::R8BH,\n    llvm::X86::R9BH,\n    llvm::X86::R10BH,\n    llvm::X86::R11BH,\n    llvm::X86::R12BH,\n    llvm::X86::R13BH,\n    llvm::X86::R14BH,\n    llvm::X86::R15BH,\n    llvm::X86::R16BH,\n    llvm::X86::R17BH,\n    llvm::X86::R18BH,\n    llvm::X86::R19BH,\n    llvm::X86::R20BH,\n    llvm::X86::R21BH,\n    llvm::X86::R22BH,\n    llvm::X86::R23BH,\n    llvm::X86::R24BH,\n    llvm::X86::R25BH,\n    llvm::X86::R26BH,\n    llvm::X86::R27BH,\n    llvm::X86::R28BH,\n    llvm::X86::R29BH,\n    llvm::X86::R30BH,\n    llvm::X86::R31BH,\n};\n\nconstexpr size_t REGISTER_1BYTE_SIZE =\n    sizeof(REGISTER_1BYTE) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_2BYTES[] = {\n    llvm::X86::AX,\n    llvm::X86::BX,\n    llvm::X86::CX,\n    llvm::X86::DX,\n    llvm::X86::SI,\n    llvm::X86::DI,\n    llvm::X86::SP,\n    llvm::X86::BP,\n    llvm::X86::R8W,\n    llvm::X86::R9W,\n    llvm::X86::R10W,\n    llvm::X86::R11W,\n    llvm::X86::R12W,\n    llvm::X86::R13W,\n    llvm::X86::R14W,\n    llvm::X86::R15W,\n    llvm::X86::R16W,\n    llvm::X86::R17W,\n    llvm::X86::R18W,\n    llvm::X86::R19W,\n    llvm::X86::R20W,\n    llvm::X86::R21W,\n    llvm::X86::R22W,\n    llvm::X86::R23W,\n    llvm::X86::R24W,\n    llvm::X86::R25W,\n    llvm::X86::R26W,\n    llvm::X86::R27W,\n    llvm::X86::R28W,\n    llvm::X86::R29W,\n    llvm::X86::R30W,\n    llvm::X86::R31W,\n    llvm::X86::IP,\n    llvm::X86::FPCW,\n    llvm::X86::FPSW,\n    // artificial\n    llvm::X86::HAX,\n    llvm::X86::HBX,\n    llvm::X86::HCX,\n    llvm::X86::HDX,\n    llvm::X86::HSI,\n    llvm::X86::HDI,\n    llvm::X86::HBP,\n    llvm::X86::HSP,\n    llvm::X86::HIP,\n    llvm::X86::R8WH,\n    llvm::X86::R9WH,\n    llvm::X86::R10WH,\n    llvm::X86::R11WH,\n    llvm::X86::R12WH,\n    llvm::X86::R13WH,\n    llvm::X86::R14WH,\n    llvm::X86::R15WH,\n    llvm::X86::R16WH,\n    llvm::X86::R17WH,\n    llvm::X86::R18WH,\n    llvm::X86::R19WH,\n    llvm::X86::R20WH,\n    llvm::X86::R21WH,\n    llvm::X86::R22WH,\n    llvm::X86::R23WH,\n    llvm::X86::R24WH,\n    llvm::X86::R25WH,\n    llvm::X86::R26WH,\n    llvm::X86::R27WH,\n    llvm::X86::R28WH,\n    llvm::X86::R29WH,\n    llvm::X86::R30WH,\n    llvm::X86::R31WH,\n    // segment\n    llvm::X86::CS,\n    llvm::X86::DS,\n    llvm::X86::ES,\n    llvm::X86::FS,\n    llvm::X86::FS_BASE,\n    llvm::X86::GS,\n    llvm::X86::GS_BASE,\n    llvm::X86::SS,\n};\n\nconstexpr size_t REGISTER_2BYTES_SIZE =\n    sizeof(REGISTER_2BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_4BYTES[] = {\n    llvm::X86::EAX,\n    llvm::X86::EBX,\n    llvm::X86::ECX,\n    llvm::X86::EDX,\n    llvm::X86::ESI,\n    llvm::X86::EDI,\n    llvm::X86::ESP,\n    llvm::X86::EBP,\n    llvm::X86::EIP,\n    llvm::X86::R8D,\n    llvm::X86::R9D,\n    llvm::X86::R10D,\n    llvm::X86::R11D,\n    llvm::X86::R12D,\n    llvm::X86::R13D,\n    llvm::X86::R14D,\n    llvm::X86::R15D,\n    llvm::X86::R16D,\n    llvm::X86::R17D,\n    llvm::X86::R18D,\n    llvm::X86::R19D,\n    llvm::X86::R20D,\n    llvm::X86::R21D,\n    llvm::X86::R22D,\n    llvm::X86::R23D,\n    llvm::X86::R24D,\n    llvm::X86::R25D,\n    llvm::X86::R26D,\n    llvm::X86::R27D,\n    llvm::X86::R28D,\n    llvm::X86::R29D,\n    llvm::X86::R30D,\n    llvm::X86::R31D,\n    // RFLAGS isn't defined in llvm, the upper 32bits is never used\n    llvm::X86::EFLAGS,\n    llvm::X86::_EFLAGS,\n    llvm::X86::EIZ,\n#if defined(QBDI_ARCH_X86)\n    // shadow stack pointer\n    llvm::X86::SSP,\n#endif\n    llvm::X86::MXCSR,\n};\n\nconstexpr size_t REGISTER_4BYTES_SIZE =\n    sizeof(REGISTER_4BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_8BYTES[] = {\n    llvm::X86::RAX,\n    llvm::X86::RBX,\n    llvm::X86::RCX,\n    llvm::X86::RDX,\n    llvm::X86::RSI,\n    llvm::X86::RDI,\n    llvm::X86::RSP,\n    llvm::X86::RBP,\n    llvm::X86::RIP,\n    llvm::X86::R8,\n    llvm::X86::R9,\n    llvm::X86::R10,\n    llvm::X86::R11,\n    llvm::X86::R12,\n    llvm::X86::R13,\n    llvm::X86::R14,\n    llvm::X86::R15,\n    llvm::X86::R16,\n    llvm::X86::R17,\n    llvm::X86::R18,\n    llvm::X86::R19,\n    llvm::X86::R20,\n    llvm::X86::R21,\n    llvm::X86::R22,\n    llvm::X86::R23,\n    llvm::X86::R24,\n    llvm::X86::R25,\n    llvm::X86::R26,\n    llvm::X86::R27,\n    llvm::X86::R28,\n    llvm::X86::R29,\n    llvm::X86::R30,\n    llvm::X86::R31,\n    llvm::X86::RFLAGS,\n    llvm::X86::MM0,\n    llvm::X86::MM1,\n    llvm::X86::MM2,\n    llvm::X86::MM3,\n    llvm::X86::MM4,\n    llvm::X86::MM5,\n    llvm::X86::MM6,\n    llvm::X86::MM7,\n    llvm::X86::RIZ,\n#if defined(QBDI_ARCH_X86_64)\n    // shadow stack pointer\n    llvm::X86::SSP,\n#endif\n};\n\nconstexpr size_t REGISTER_8BYTES_SIZE =\n    sizeof(REGISTER_8BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_10BYTES[] = {\n    llvm::X86::ST0, llvm::X86::ST1, llvm::X86::ST2, llvm::X86::ST3,\n    llvm::X86::ST4, llvm::X86::ST5, llvm::X86::ST6, llvm::X86::ST7,\n};\n\nconstexpr size_t REGISTER_10BYTES_SIZE =\n    sizeof(REGISTER_10BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_16BYTES[] = {\n    llvm::X86::XMM0,  llvm::X86::XMM1,  llvm::X86::XMM2,  llvm::X86::XMM3,\n    llvm::X86::XMM4,  llvm::X86::XMM5,  llvm::X86::XMM6,  llvm::X86::XMM7,\n    llvm::X86::XMM8,  llvm::X86::XMM9,  llvm::X86::XMM10, llvm::X86::XMM11,\n    llvm::X86::XMM12, llvm::X86::XMM13, llvm::X86::XMM14, llvm::X86::XMM15,\n    llvm::X86::XMM16, llvm::X86::XMM17, llvm::X86::XMM18, llvm::X86::XMM19,\n    llvm::X86::XMM20, llvm::X86::XMM21, llvm::X86::XMM22, llvm::X86::XMM23,\n    llvm::X86::XMM24, llvm::X86::XMM25, llvm::X86::XMM26, llvm::X86::XMM27,\n    llvm::X86::XMM28, llvm::X86::XMM29, llvm::X86::XMM30, llvm::X86::XMM31,\n};\n\nconstexpr size_t REGISTER_16BYTES_SIZE =\n    sizeof(REGISTER_16BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_32BYTES[] = {\n    llvm::X86::YMM0,  llvm::X86::YMM1,  llvm::X86::YMM2,  llvm::X86::YMM3,\n    llvm::X86::YMM4,  llvm::X86::YMM5,  llvm::X86::YMM6,  llvm::X86::YMM7,\n    llvm::X86::YMM8,  llvm::X86::YMM9,  llvm::X86::YMM10, llvm::X86::YMM11,\n    llvm::X86::YMM12, llvm::X86::YMM13, llvm::X86::YMM14, llvm::X86::YMM15,\n    llvm::X86::YMM16, llvm::X86::YMM17, llvm::X86::YMM18, llvm::X86::YMM19,\n    llvm::X86::YMM20, llvm::X86::YMM21, llvm::X86::YMM22, llvm::X86::YMM23,\n    llvm::X86::YMM24, llvm::X86::YMM25, llvm::X86::YMM26, llvm::X86::YMM27,\n    llvm::X86::YMM28, llvm::X86::YMM29, llvm::X86::YMM30, llvm::X86::YMM31,\n};\n\nconstexpr size_t REGISTER_32BYTES_SIZE =\n    sizeof(REGISTER_32BYTES) / sizeof(uint16_t);\n\nconstexpr uint16_t REGISTER_64BYTES[] = {\n    llvm::X86::ZMM0,  llvm::X86::ZMM1,  llvm::X86::ZMM2,  llvm::X86::ZMM3,\n    llvm::X86::ZMM4,  llvm::X86::ZMM5,  llvm::X86::ZMM6,  llvm::X86::ZMM7,\n    llvm::X86::ZMM8,  llvm::X86::ZMM9,  llvm::X86::ZMM10, llvm::X86::ZMM11,\n    llvm::X86::ZMM12, llvm::X86::ZMM13, llvm::X86::ZMM14, llvm::X86::ZMM15,\n    llvm::X86::ZMM16, llvm::X86::ZMM17, llvm::X86::ZMM18, llvm::X86::ZMM19,\n    llvm::X86::ZMM20, llvm::X86::ZMM21, llvm::X86::ZMM22, llvm::X86::ZMM23,\n    llvm::X86::ZMM24, llvm::X86::ZMM25, llvm::X86::ZMM26, llvm::X86::ZMM27,\n    llvm::X86::ZMM28, llvm::X86::ZMM29, llvm::X86::ZMM30, llvm::X86::ZMM31,\n};\n\nconstexpr size_t REGISTER_64BYTES_SIZE =\n    sizeof(REGISTER_64BYTES) / sizeof(uint16_t);\n\nconstexpr int8_t getGPRPositionConst(size_t reg) {\n  switch (reg) {\n    case llvm::X86::AL:\n    case llvm::X86::AH:\n    case llvm::X86::AX:\n    case llvm::X86::HAX:\n    case llvm::X86::EAX:\n    case llvm::X86::RAX:\n      return 0;\n    case llvm::X86::BL:\n    case llvm::X86::BH:\n    case llvm::X86::BX:\n    case llvm::X86::HBX:\n    case llvm::X86::EBX:\n    case llvm::X86::RBX:\n      return 1;\n    case llvm::X86::CL:\n    case llvm::X86::CH:\n    case llvm::X86::CX:\n    case llvm::X86::HCX:\n    case llvm::X86::ECX:\n    case llvm::X86::RCX:\n      return 2;\n    case llvm::X86::DL:\n    case llvm::X86::DH:\n    case llvm::X86::DX:\n    case llvm::X86::HDX:\n    case llvm::X86::EDX:\n    case llvm::X86::RDX:\n      return 3;\n    case llvm::X86::SIL:\n    case llvm::X86::SIH:\n    case llvm::X86::SI:\n    case llvm::X86::HSI:\n    case llvm::X86::ESI:\n    case llvm::X86::RSI:\n      return 4;\n    case llvm::X86::DIL:\n    case llvm::X86::DIH:\n    case llvm::X86::DI:\n    case llvm::X86::HDI:\n    case llvm::X86::EDI:\n    case llvm::X86::RDI:\n      return 5;\n    case llvm::X86::BPL:\n    case llvm::X86::BPH:\n    case llvm::X86::BP:\n    case llvm::X86::HBP:\n    case llvm::X86::EBP:\n    case llvm::X86::RBP:\n      return REG_BP;\n    case llvm::X86::SPL:\n    case llvm::X86::SPH:\n    case llvm::X86::SP:\n    case llvm::X86::HSP:\n    case llvm::X86::ESP:\n    case llvm::X86::RSP:\n      return REG_SP;\n    case llvm::X86::IP:\n    case llvm::X86::HIP:\n    case llvm::X86::EIP:\n    case llvm::X86::RIP:\n      return REG_PC;\n#if defined(QBDI_ARCH_X86_64)\n    case llvm::X86::R8:\n    case llvm::X86::R8B:\n    case llvm::X86::R8BH:\n    case llvm::X86::R8D:\n    case llvm::X86::R8W:\n    case llvm::X86::R8WH:\n      return 6;\n    case llvm::X86::R9:\n    case llvm::X86::R9B:\n    case llvm::X86::R9BH:\n    case llvm::X86::R9D:\n    case llvm::X86::R9W:\n    case llvm::X86::R9WH:\n      return 7;\n    case llvm::X86::R10:\n    case llvm::X86::R10B:\n    case llvm::X86::R10BH:\n    case llvm::X86::R10D:\n    case llvm::X86::R10W:\n    case llvm::X86::R10WH:\n      return 8;\n    case llvm::X86::R11:\n    case llvm::X86::R11B:\n    case llvm::X86::R11BH:\n    case llvm::X86::R11D:\n    case llvm::X86::R11W:\n    case llvm::X86::R11WH:\n      return 9;\n    case llvm::X86::R12:\n    case llvm::X86::R12B:\n    case llvm::X86::R12BH:\n    case llvm::X86::R12D:\n    case llvm::X86::R12W:\n    case llvm::X86::R12WH:\n      return 10;\n    case llvm::X86::R13:\n    case llvm::X86::R13B:\n    case llvm::X86::R13BH:\n    case llvm::X86::R13D:\n    case llvm::X86::R13W:\n    case llvm::X86::R13WH:\n      return 11;\n    case llvm::X86::R14:\n    case llvm::X86::R14B:\n    case llvm::X86::R14BH:\n    case llvm::X86::R14D:\n    case llvm::X86::R14W:\n    case llvm::X86::R14WH:\n      return 12;\n    case llvm::X86::R15:\n    case llvm::X86::R15B:\n    case llvm::X86::R15BH:\n    case llvm::X86::R15D:\n    case llvm::X86::R15W:\n    case llvm::X86::R15WH:\n      return 13;\n#endif // QBDI_ARCH_X86_64\n    default:\n      return -1;\n  }\n}\n\nstruct RegisterInfoArray {\n  uint8_t size[llvm::X86::NUM_TARGET_REGS] = {0};\n  int8_t pos[llvm::X86::NUM_TARGET_REGS] = {0};\n\n  constexpr RegisterInfoArray() {\n    for (size_t i = 0; i < REGISTER_1BYTE_SIZE; i++) {\n      size[REGISTER_1BYTE[i]] = 1;\n    }\n    for (size_t i = 0; i < REGISTER_2BYTES_SIZE; i++) {\n      size[REGISTER_2BYTES[i]] = 2;\n    }\n    for (size_t i = 0; i < REGISTER_4BYTES_SIZE; i++) {\n      size[REGISTER_4BYTES[i]] = 4;\n    }\n    for (size_t i = 0; i < REGISTER_8BYTES_SIZE; i++) {\n      size[REGISTER_8BYTES[i]] = 8;\n    }\n    for (size_t i = 0; i < REGISTER_10BYTES_SIZE; i++) {\n      size[REGISTER_10BYTES[i]] = 10;\n    }\n    for (size_t i = 0; i < REGISTER_16BYTES_SIZE; i++) {\n      size[REGISTER_16BYTES[i]] = 16;\n    }\n    for (size_t i = 0; i < REGISTER_32BYTES_SIZE; i++) {\n      size[REGISTER_32BYTES[i]] = 32;\n    }\n    for (size_t i = 0; i < REGISTER_64BYTES_SIZE; i++) {\n      size[REGISTER_64BYTES[i]] = 64;\n    }\n    for (size_t i = 0; i < llvm::X86::NUM_TARGET_REGS; i++) {\n      pos[i] = getGPRPositionConst(i);\n    }\n  }\n\n  inline uint8_t getSize(RegLLVM reg_) const {\n    const size_t reg = reg_.getValue();\n    if (reg < llvm::X86::NUM_TARGET_REGS)\n      return size[reg];\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return -1;\n  }\n\n  inline int8_t getPos(RegLLVM reg_) const {\n    const size_t reg = reg_.getValue();\n    if (reg < llvm::X86::NUM_TARGET_REGS)\n      return pos[reg];\n\n    QBDI_ERROR(\"No register {}\", reg);\n    return -1;\n  }\n};\n\n} // anonymous namespace\n\nstatic constexpr RegisterInfoArray arrayInfo;\n\nuint8_t getRegisterSize(RegLLVM reg) { return arrayInfo.getSize(reg); }\n\nuint8_t getRegisterBaseOffset(RegLLVM reg) {\n  switch (reg.getValue()) {\n    case llvm::X86::AH:\n    case llvm::X86::BH:\n    case llvm::X86::CH:\n    case llvm::X86::DH:\n      return 8;\n    default:\n      return 0;\n  }\n}\n\nuint8_t getRegisterPacked(RegLLVM reg) { return 1; }\n\nuint8_t getRegisterSpaced(RegLLVM reg) { return 1; }\n\nsize_t getGPRPosition(RegLLVM reg) { return arrayInfo.getPos(reg); }\n\nRegLLVM getUpperRegister(RegLLVM reg, size_t pos) {\n  if (pos != 0) {\n    return llvm::X86::NoRegister;\n  }\n  int8_t p = getGPRPosition(reg);\n  if (p == -1) {\n    return reg;\n  }\n  return GPR_ID[p];\n}\n\nRegLLVM getPackedRegister(RegLLVM reg, size_t pos) {\n  if (pos != 0) {\n    return llvm::X86::NoRegister;\n  }\n  return reg;\n}\n\nvoid fixLLVMUsedGPR(const llvm::MCInst &inst, const LLVMCPU &llvmcpu,\n                    std::array<RegisterUsage, NUM_GPR> &arr,\n                    std::map<RegLLVM, RegisterUsage> &m) {\n  switch (inst.getOpcode()) {\n    case llvm::X86::LOOP:\n    case llvm::X86::LOOPE:\n    case llvm::X86::LOOPNE:\n      arr[2] |= RegisterUsed | RegisterSet;\n      break;\n    default:\n      break;\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/RelocatableInst_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdint.h>\n\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/X86_64/Layer2_X86_64.h\"\n#include \"Patch/X86_64/RelocatableInst_X86_64.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\n// Generic RelocatableInst that must be implemented by each target\n\n// RelocTag\n// ========\n\nllvm::MCInst RelocTag::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  QBDI_ERROR(\"Internal Error: Relocate a Tag instruction.\");\n  return nop();\n}\n\n// LoadShadow\n// ==========\n\nllvm::MCInst LoadShadow::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id = execBlock->getLastShadow(tag);\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n\n  if constexpr (is_x86_64) {\n    return mov64rm(reg, Reg(REG_PC), 1, 0,\n                   execBlock->getDataBlockOffset() + shadowOffset - 7, 0);\n  } else {\n    return mov32rm(reg, 0, 0, 0, execBlock->getDataBlockBase() + shadowOffset,\n                   0);\n  }\n}\n\nint LoadShadow::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    return 7;\n  } else {\n    return 6;\n  }\n}\n\n// StoreShadow\n// ===========\n\nllvm::MCInst StoreShadow::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  uint16_t id;\n  if (create) {\n    id = execBlock->newShadow(tag);\n  } else {\n    id = execBlock->getLastShadow(tag);\n  }\n  unsigned int shadowOffset = execBlock->getShadowOffset(id);\n\n  if constexpr (is_x86_64) {\n    return mov64mr(Reg(REG_PC), 1, 0,\n                   execBlock->getDataBlockOffset() + shadowOffset - 7, 0, reg);\n  } else {\n    return mov32mr(0, 0, 0, execBlock->getDataBlockBase() + shadowOffset, 0,\n                   reg);\n  }\n}\n\nint StoreShadow::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    return 7;\n  } else {\n    return 6;\n  }\n}\n\n// LoadDataBlock\n// =============\n\nllvm::MCInst LoadDataBlock::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n\n  if constexpr (is_x86_64) {\n    return mov64rm(reg, Reg(REG_PC), 1, 0,\n                   execBlock->getDataBlockOffset() + offset - 7, 0);\n  } else {\n    return mov32rm(reg, 0, 0, 0, execBlock->getDataBlockBase() + offset, 0);\n  }\n}\n\nint LoadDataBlock::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    return 7;\n  } else {\n    return 6;\n  }\n}\n\n// StoreDataBlock\n// ==============\n\nllvm::MCInst StoreDataBlock::reloc(ExecBlock *execBlock,\n                                   CPUMode cpumode) const {\n\n  if constexpr (is_x86_64) {\n    return mov64mr(Reg(REG_PC), 1, 0,\n                   execBlock->getDataBlockOffset() + offset - 7, 0, reg);\n  } else {\n    return mov32mr(0, 0, 0, execBlock->getDataBlockBase() + offset, 0, reg);\n  }\n}\n\nint StoreDataBlock::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    return 7;\n  } else {\n    return 6;\n  }\n}\n\n// MovReg\n// ======\n\nllvm::MCInst MovReg::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  if constexpr (is_x86_64)\n    return mov64rr(dst, src);\n  else\n    return mov32rr(dst, src);\n}\n\nint MovReg::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    return 3;\n  } else {\n    return 2;\n  }\n}\n\n// LoadImm\n// =======\n\nllvm::MCInst LoadImm::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  if constexpr (is_x86_64) {\n    if (imm < -0x80000000ull or 0x80000000ull <= imm) {\n      return mov64ri(reg, imm);\n    } else {\n      return mov64ri32(reg, imm);\n    }\n  } else {\n    return mov32ri(reg, imm);\n  }\n}\n\nint LoadImm::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    if (imm < -0x80000000ull or 0x80000000ull <= imm) {\n      return 10;\n    } else {\n      return 7;\n    }\n  } else {\n    return 5;\n  }\n}\n\n// InstId\n// ======\n\nllvm::MCInst InstId::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  if constexpr (is_x86_64) {\n    return mov64ri32(reg, execBlock->getNextInstID());\n  } else {\n    return mov32ri(reg, execBlock->getNextInstID());\n  }\n}\n\nint InstId::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    return 7;\n  } else {\n    return 5;\n  }\n}\n\n// Target Specific RelocatableInst\n\n// EpilogueJump\n// ============\n\nllvm::MCInst EpilogueJump::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  return jmp(execBlock->getEpilogueOffset() - 1);\n}\n\nint EpilogueJump::getSize(const LLVMCPU &llvmcpu) const { return 5; }\n\n// SetRegtoPCRel\n// =============\n\nllvm::MCInst SetRegtoPCRel::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  if constexpr (is_x86_64)\n    return mov64ri(reg, offset + execBlock->getCurrentPC());\n  else\n    return mov32ri(reg, offset + execBlock->getCurrentPC());\n}\n\nint SetRegtoPCRel::getSize(const LLVMCPU &llvmcpu) const {\n  if constexpr (is_x86_64) {\n    return 10;\n  } else {\n    return 5;\n  }\n}\n\n// DataBlockRel\n// ============\n\nllvm::MCInst DataBlockRel::reloc(ExecBlock *execBlock, CPUMode cpumode) const {\n  llvm::MCInst res = inst;\n  QBDI_REQUIRE_ABORT(opn < res.getNumOperands(), \"Invalid operand {}\", opn);\n  QBDI_REQUIRE_ABORT(res.getOperand(opn).isImm(), \"Unexpected operand type\");\n\n  res.getOperand(opn).setImm(offset + execBlock->getDataBlockOffset());\n  return res;\n}\n\n// DataBlockAbsRel\n// ===============\n\nllvm::MCInst DataBlockAbsRel::reloc(ExecBlock *execBlock,\n                                    CPUMode cpumode) const {\n  llvm::MCInst res = inst;\n  QBDI_REQUIRE_ABORT(opn < res.getNumOperands(), \"Invalid operand {}\", opn);\n  QBDI_REQUIRE_ABORT(res.getOperand(opn).isImm(), \"Unexpected operand type\");\n\n  res.getOperand(opn).setImm(execBlock->getDataBlockBase() + offset);\n  return res;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Patch/X86_64/RelocatableInst_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef RELOCATABLEINST_X86_64_H\n#define RELOCATABLEINST_X86_64_H\n\n#include <memory>\n#include <utility>\n\n#include \"llvm/MC/MCInst.h\"\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/State.h\"\n#include \"Patch/PatchUtils.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\nclass ExecBlock;\n\nclass NoRelocSized : public AutoClone<RelocatableInst, NoRelocSized> {\n  llvm::MCInst inst;\n  int size;\n\npublic:\n  NoRelocSized(llvm::MCInst &&inst, int size)\n      : inst(std::forward<llvm::MCInst>(inst)), size(size) {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override {\n    return inst;\n  }\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return size; };\n};\n\nclass EpilogueJump : public AutoClone<RelocatableInst, EpilogueJump> {\n\npublic:\n  EpilogueJump() : AutoClone<RelocatableInst, EpilogueJump>() {}\n\n  // Set an operand to epilogueOffset + offset\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass SetRegtoPCRel : public AutoClone<RelocatableInst, SetRegtoPCRel> {\n  Reg reg;\n  rword offset;\n\npublic:\n  SetRegtoPCRel(Reg reg, rword offset)\n      : AutoClone<RelocatableInst, SetRegtoPCRel>(), reg(reg), offset(offset) {}\n\n  // set reg to currentPC + offset\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override;\n};\n\nclass DataBlockRel : public AutoClone<RelocatableInst, DataBlockRel> {\n  llvm::MCInst inst;\n  unsigned int opn;\n  rword offset;\n  int size;\n\npublic:\n  DataBlockRel(llvm::MCInst &&inst, unsigned int opn, rword offset, int size)\n      : AutoClone<RelocatableInst, DataBlockRel>(),\n        inst(std::forward<llvm::MCInst>(inst)), opn(opn), offset(offset),\n        size(size) {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return size; }\n};\n\nclass DataBlockAbsRel : public AutoClone<RelocatableInst, DataBlockAbsRel> {\n  llvm::MCInst inst;\n  unsigned int opn;\n  rword offset;\n  int size;\n\npublic:\n  DataBlockAbsRel(llvm::MCInst &&inst, unsigned int opn, rword offset, int size)\n      : AutoClone<RelocatableInst, DataBlockAbsRel>(),\n        inst(std::forward<llvm::MCInst>(inst)), opn(opn), offset(offset),\n        size(size) {}\n\n  llvm::MCInst reloc(ExecBlock *execBlock, CPUMode cpumode) const override;\n\n  int getSize(const LLVMCPU &llvmcpu) const override { return size; }\n};\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Patch/X86_64/TempManagerImpl_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef TempManager_X86_64_H\n#define TempManager_X86_64_H\n\n#include <set>\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nstatic const std::set<Reg> TempManagerUnrestoreGPR = {};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/Patch/X86_64/TempManager_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <set>\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchGenerator.h\"\n#include \"Patch/RelocatableInst.h\"\n#include \"Patch/TempManager.h\"\n#include \"Patch/Types.h\"\n\nnamespace QBDI {\n\nvoid TempManager::generateSaveRestoreInstructions(\n    unsigned unrestoredRegNum, RelocatableInst::UniquePtrVec &saveInst,\n    RelocatableInst::UniquePtrVec &restoreInst, Reg::Vec &unrestoredReg) const {\n\n  saveInst.clear();\n  restoreInst.clear();\n  unrestoredReg.clear();\n\n  Reg::Vec usedRegisters = getUsedRegisters();\n\n  for (Reg r : usedRegisters) {\n    if (shouldRestore(r)) {\n      append(saveInst, SaveReg(r, Offset(r)).genReloc(*patch.llvmcpu));\n      if (unrestoredReg.size() < unrestoredRegNum) {\n        unrestoredReg.push_back(r);\n      } else {\n        append(restoreInst, LoadReg(r, Offset(r)).genReloc(*patch.llvmcpu));\n      }\n    } else {\n      unrestoredReg.push_back(r);\n    }\n  }\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/AARCH64/CMakeLists.txt",
    "content": "set(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysis_AARCH64.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/linux-android-AARCH64.s\")\nelseif(QBDI_PLATFORM_MACOS)\n  if(QBDI_PTRAUTH)\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/macos-AARCH64e.s\")\n  else()\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/macos-AARCH64.s\")\n  endif()\n  list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/System_OSX.cpp\"\n       \"${CMAKE_CURRENT_LIST_DIR}/System_darwin.cpp\")\n\nelseif(QBDI_PLATFORM_IOS)\n  if(QBDI_PTRAUTH)\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ios-AARCH64e.s\")\n  else()\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/ios-AARCH64.s\")\n  endif()\n  list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/System_darwin.cpp\"\n       \"${CMAKE_CURRENT_LIST_DIR}/System_iOS.cpp\")\n\nelse()\n  message(FATAL_ERROR \"No stub for ${QBDI_PLATFORM} (${QBDI_ARCH})\")\nendif()\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Utility/AARCH64/InstAnalysis_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <map>\n#include <string>\n\n#include \"Utils/AArch64BaseInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"Patch/AARCH64/InstInfo_AARCH64.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/InstAnalysis_prive.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\nnamespace InstructionAnalysis {\n\nConditionType ConditionLLVM2QBDI(unsigned cond) {\n  switch (cond) {\n    case llvm::AArch64CC::CondCode::EQ:\n      return CONDITION_EQUALS;\n    case llvm::AArch64CC::CondCode::NE:\n      return CONDITION_NOT_EQUALS;\n    case llvm::AArch64CC::CondCode::HS:\n      return CONDITION_ABOVE_EQUALS;\n    case llvm::AArch64CC::CondCode::LO:\n      return CONDITION_BELOW;\n    case llvm::AArch64CC::CondCode::MI:\n      return CONDITION_SIGN;\n    case llvm::AArch64CC::CondCode::PL:\n      return CONDITION_NOT_SIGN;\n    case llvm::AArch64CC::CondCode::VS:\n      return CONDITION_OVERFLOW;\n    case llvm::AArch64CC::CondCode::VC:\n      return CONDITION_NOT_OVERFLOW;\n    case llvm::AArch64CC::CondCode::HI:\n      return CONDITION_ABOVE;\n    case llvm::AArch64CC::CondCode::LS:\n      return CONDITION_BELOW_EQUALS;\n    case llvm::AArch64CC::CondCode::GE:\n      return CONDITION_GREAT_EQUALS;\n    case llvm::AArch64CC::CondCode::LT:\n      return CONDITION_LESS;\n    case llvm::AArch64CC::CondCode::GT:\n      return CONDITION_GREAT;\n    case llvm::AArch64CC::CondCode::LE:\n      return CONDITION_LESS_EQUALS;\n    case llvm::AArch64CC::CondCode::AL:\n    case llvm::AArch64CC::CondCode::NV:\n      return CONDITION_ALWAYS;\n    default:\n      QBDI_ABORT(\"Unsupported LLVM condition {}\", cond);\n  }\n}\n\nstatic unsigned getFlagOperand(unsigned opcode) {\n  switch (opcode) {\n    case llvm::AArch64::Bcc:\n      return 0;\n    case llvm::AArch64::CCMNWi:\n    case llvm::AArch64::CCMNWr:\n    case llvm::AArch64::CCMNXi:\n    case llvm::AArch64::CCMNXr:\n    case llvm::AArch64::CCMPWi:\n    case llvm::AArch64::CCMPWr:\n    case llvm::AArch64::CCMPXi:\n    case llvm::AArch64::CCMPXr:\n    case llvm::AArch64::CSELWr:\n    case llvm::AArch64::CSELXr:\n    case llvm::AArch64::CSINCWr:\n    case llvm::AArch64::CSINCXr:\n    case llvm::AArch64::CSINVWr:\n    case llvm::AArch64::CSINVXr:\n    case llvm::AArch64::CSNEGWr:\n    case llvm::AArch64::CSNEGXr:\n    case llvm::AArch64::F128CSEL:\n    case llvm::AArch64::FCCMPDrr:\n    case llvm::AArch64::FCCMPEDrr:\n    case llvm::AArch64::FCCMPEHrr:\n    case llvm::AArch64::FCCMPESrr:\n    case llvm::AArch64::FCCMPHrr:\n    case llvm::AArch64::FCCMPSrr:\n    case llvm::AArch64::FCSELDrrr:\n    case llvm::AArch64::FCSELHrrr:\n    case llvm::AArch64::FCSELSrrr:\n      return 3;\n    default:\n      return -1;\n  }\n}\n\nvoid analyseCondition(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                      const llvm::MCInstrDesc &desc, const LLVMCPU &llvmcpu) {\n\n  instAnalysis->condition = CONDITION_NONE;\n\n  unsigned n = getFlagOperand(inst.getOpcode());\n  if (n == ((unsigned)-1)) {\n    return;\n  }\n  QBDI_REQUIRE_ACTION(n < inst.getNumOperands(), return);\n  QBDI_REQUIRE_ACTION(inst.getOperand(n).isImm(), return);\n\n  instAnalysis->condition =\n      ConditionLLVM2QBDI(static_cast<unsigned>(inst.getOperand(n).getImm()));\n}\n\nbool isFlagOperand(unsigned opcode, unsigned opNum,\n                   const llvm::MCOperandInfo &opdesc) {\n  unsigned n = getFlagOperand(opcode);\n  if (n == ((unsigned)-1)) {\n    return false;\n  }\n\n  return n == opNum;\n}\n\nunsigned getBias(const llvm::MCInstrDesc &desc) {\n  unsigned NumDefs = desc.getNumDefs();\n  unsigned NumOps = desc.getNumOperands();\n  unsigned bias = 0;\n\n  for (unsigned opDef = 0; opDef < NumDefs; opDef++) {\n    bool found = false;\n    for (unsigned op = opDef + 1; op < NumOps; op++) {\n      if (desc.getOperandConstraint(op, llvm::MCOI::TIED_TO) == (int)opDef) {\n        bias = opDef + 1;\n        found = true;\n        break;\n      }\n    }\n    if (not found) {\n      break;\n    }\n  }\n\n  return bias;\n}\n\nunsigned getAdditionnalOperandNumber(const llvm::MCInst &inst,\n                                     const llvm::MCInstrDesc &desc) {\n\n  return 0;\n}\n\nvoid getAdditionnalOperand(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                           const llvm::MCInstrDesc &desc,\n                           const llvm::MCRegisterInfo &MRI) {}\n\n} // namespace InstructionAnalysis\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/AARCH64/System_OSX.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string>\n#include <system_error>\n\n#include \"llvm/ADT/StringMap.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/iterator.h\"\n#include \"llvm/Support/Memory.h\"\n#include \"llvm/TargetParser/Host.h\"\n#include \"llvm/TargetParser/SubtargetFeature.h\"\n\n#include \"QBDI/Config.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\nnamespace QBDI {\n\nbool isRWXSupported() { return false; }\nbool isRWRXSupported() { return true; }\n\nllvm::sys::MemoryBlock\nallocateMappedMemory(size_t numBytes,\n                     const llvm::sys::MemoryBlock *const nearBlock,\n                     unsigned pFlags, std::error_code &ec) {\n  // forward to llvm function\n  return llvm::sys::Memory::allocateMappedMemory(numBytes, nearBlock, pFlags,\n                                                 ec);\n}\n\nvoid releaseMappedMemory(llvm::sys::MemoryBlock &block) {\n  llvm::sys::Memory::releaseMappedMemory(block);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/AARCH64/System_darwin.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <stdlib.h>\n\n#include <mach/host_info.h>\n#include <mach/mach.h>\n#include <mach/mach_host.h>\n\n#include \"llvm/ADT/StringMap.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/iterator.h\"\n#include \"llvm/TargetParser/Host.h\"\n#include \"llvm/TargetParser/SubtargetFeature.h\"\n\n#include \"QBDI/Config.h\"\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\nnamespace QBDI {\n\nstatic const std::string _getHostCPUName() {\n  host_basic_info_data_t hostInfo;\n  mach_msg_type_number_t infoCount;\n\n  infoCount = HOST_BASIC_INFO_COUNT;\n  host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo,\n            &infoCount);\n\n  // ARM32 kernel\n  if (hostInfo.cpu_type == CPU_TYPE_ARM) {\n    switch (hostInfo.cpu_subtype) {\n      case CPU_SUBTYPE_ARM_V4T:\n        return \"arm710t\";\n      case CPU_SUBTYPE_ARM_V6:\n        return \"arm1136j-s\";\n      case CPU_SUBTYPE_ARM_V5TEJ:\n        return \"arm9e\";\n      case CPU_SUBTYPE_ARM_XSCALE:\n        return \"xscale\";\n      case CPU_SUBTYPE_ARM_V7:\n        return \"cortex-a8\";\n      case CPU_SUBTYPE_ARM_V7F:\n        return \"cortex-a9\";\n      case CPU_SUBTYPE_ARM_V7S:\n        return \"swift\";\n      case CPU_SUBTYPE_ARM_V8:\n        return \"cortex-a53\";\n      default:\n        return \"arm710t\"; // Minimum model on iOS\n    }\n  }\n#ifdef CPU_TYPE_ARM64\n  // ARM64 kernel\n  else if (hostInfo.cpu_type == CPU_TYPE_ARM64) {\n#if defined(QBDI_ARCH_AARCH64)\n    switch (hostInfo.cpu_subtype) {\n      case CPU_SUBTYPE_ARM64_ALL:\n      case CPU_SUBTYPE_ARM64_V8:\n      default:\n        return \"cyclone\";\n      case CPU_SUBTYPE_ARM64E:\n        return \"apple-a12\";\n    }\n#endif\n    // armv7 / armv7s on ARM64 kernel\n    return \"swift\";\n  }\n#endif\n  QBDI_WARN(\"Unknown cpu type, using generic\");\n  return \"generic\";\n}\n\nconst std::string getHostCPUName() {\n  static std::once_flag cpuflag;\n  static std::string cpuname;\n\n  std::call_once(cpuflag, []() { cpuname = _getHostCPUName(); });\n\n  return cpuname;\n}\n\nconst std::vector<std::string> getHostCPUFeatures() {\n  std::vector<std::string> mattrs = {};\n  llvm::StringMap<bool> features = llvm::sys::getHostCPUFeatures();\n\n  bool ret = !features.empty();\n\n  if (!ret) {\n    features.clear();\n    const std::string cpuname = getHostCPUName();\n    if (cpuname == \"apple-a12\" or cpuname == \"cyclone\") {\n      features[\"fp-armv8\"] = true;\n      features[\"fullfp16\"] = true;\n      features[\"neon\"] = true;\n      if (cpuname == \"apple-a12\") {\n        features[\"v8.3a\"] = true;\n        features[\"pauth\"] = true;\n      }\n    } else {\n      QBDI_WARN(\"Fail to detect CPUHostFeatures\");\n    }\n  }\n\n  const char *fixupFeatures = getenv(\"QBDI_FIXUP_FEATURES\");\n  if (fixupFeatures != nullptr) {\n    llvm::SubtargetFeatures addFeatures(fixupFeatures);\n    for (const auto &f : addFeatures.getFeatures()) {\n      if (llvm::SubtargetFeatures::hasFlag(f)) {\n        features[llvm::SubtargetFeatures::StripFlag(f)] =\n            llvm::SubtargetFeatures::isEnabled(f);\n      } else {\n        features[f] = true;\n      }\n    }\n  }\n\n  if (ret || fixupFeatures != nullptr) {\n\n    for (const auto &feat : features) {\n      QBDI_DEBUG(\"Feature {}: {}\", feat.getKeyData(), feat.getValue());\n      if (!feat.getValue()) {\n        continue;\n      }\n      mattrs.push_back(feat.getKey().str());\n    }\n  }\n\n  if constexpr (is_arm)\n    // set default ARM CPU\n    if (features.size() == 0) {\n      features.insert({\"fp16\", true});\n      features.insert({\"d16\", true});\n    }\n  // Fixing awfull LLVM API\n  if (features.count(\"fp16\") && features[\"fp16\"]) {\n    mattrs.emplace_back(\"vfp2\");\n  }\n  if (features.count(\"d16\") && features[\"d16\"]) {\n    mattrs.emplace_back(\"vfp3\");\n  }\n\n  return mattrs;\n}\n\nbool isHostCPUFeaturePresent(const char *query) {\n  std::vector<std::string> features = getHostCPUFeatures();\n  for (const std::string &feature : features) {\n    if (feature == query) {\n      return true;\n    }\n  }\n  return false;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/AARCH64/System_iOS.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <mach/host_info.h>\n#include <mach/mach.h>\n#include <mach/mach_host.h>\n#include <mach/machine.h>\n#include <mach/vm_map.h>\n#include <mutex>\n\n#include \"llvm/Support/Process.h\"\n#include \"llvm/TargetParser/Host.h\"\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#ifndef PT_TRACE_ME\n#define PT_TRACE_ME 0\n#define PT_DETACH 11\n#endif\n\nnamespace QBDI {\n\nstd::once_flag rwxflag;\nusing PF = llvm::sys::Memory::ProtectionFlags;\n\nnamespace {\n\nvm_prot_t getPageProtection(vm_address_t address_, mach_port_t &task) {\n  vm_address_t address = address_;\n  vm_size_t size = 0;\n  natural_t depth = 0;\n  kern_return_t kr;\n  struct vm_region_submap_info_64 info;\n  mach_msg_type_number_t info_count;\n  while (true) {\n    info_count = VM_REGION_SUBMAP_INFO_COUNT_64;\n    kr = vm_region_recurse_64(task, &address, &size, &depth,\n                              (vm_region_recurse_info_t)&info, &info_count);\n    if (kr != KERN_SUCCESS)\n      break;\n\n    if (info.is_submap) {\n      depth++;\n      continue;\n    } else {\n      return (info.protection &\n              (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE));\n    }\n  }\n  return 0;\n}\n\nbool cached_RWX_supported = false;\nbool cached_RW_RX_supported = false;\n\nvoid cache_RWXSupported() {\n  bool success = false;\n  mach_port_t task;\n  vm_address_t page = 0;\n  kern_return_t kr;\n  size_t pageSize = llvm::sys::Process::getPageSize().get();\n\n  task = mach_task_self();\n\n  kr = vm_allocate(task, &page, pageSize, VM_FLAGS_ANYWHERE);\n  QBDI_REQUIRE_ACTION(kr == KERN_SUCCESS, return);\n\n  llvm::sys::MemoryBlock block((void *)page, pageSize);\n\n  std::error_code ec = llvm::sys::Memory::protectMappedMemory(\n      block, PF::MF_READ | PF::MF_WRITE | PF::MF_EXEC);\n  success = ec.value() == 0;\n\n  if (success) {\n    cached_RWX_supported = getPageProtection(page, task) ==\n                           (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);\n  } else {\n    cached_RWX_supported = false;\n  }\n\n  ec =\n      llvm::sys::Memory::protectMappedMemory(block, PF::MF_READ | PF::MF_WRITE);\n  cached_RW_RX_supported = ec.value() == 0;\n  if (cached_RW_RX_supported) {\n    cached_RW_RX_supported &=\n        getPageProtection(page, task) == (VM_PROT_READ | VM_PROT_WRITE);\n  }\n  if (cached_RW_RX_supported) {\n    ec = llvm::sys::Memory::protectMappedMemory(block,\n                                                PF::MF_READ | PF::MF_EXEC);\n    cached_RW_RX_supported &= ec.value() == 0;\n  }\n  if (cached_RW_RX_supported) {\n    cached_RW_RX_supported &=\n        getPageProtection(page, task) == (VM_PROT_READ | VM_PROT_EXECUTE);\n  }\n\n  vm_deallocate(task, page, pageSize);\n\n  QBDI_DEBUG(\"Support RW_RX pages : {}\", cached_RW_RX_supported);\n  QBDI_DEBUG(\"Support RWX pages : {}\", cached_RWX_supported);\n  if (not(cached_RW_RX_supported or cached_RWX_supported)) {\n    QBDI_CRITICAL(\"Cannot create JIT compatible page\");\n  }\n}\n\n} // anonymous namespace\n\nbool isRWXSupported() {\n  std::call_once(rwxflag, cache_RWXSupported);\n\n  return cached_RWX_supported;\n}\n\nbool isRWRXSupported() {\n  std::call_once(rwxflag, cache_RWXSupported);\n\n  return cached_RW_RX_supported;\n}\n\nllvm::sys::MemoryBlock\nallocateMappedMemory(size_t numBytes,\n                     const llvm::sys::MemoryBlock *const nearBlock,\n                     unsigned pFlags, std::error_code &ec) {\n  // Set default error code\n  ec = std::error_code(ENOMEM, std::system_category());\n  // create an empty memory block (in case of failure)\n  llvm::sys::MemoryBlock empty = llvm::sys::MemoryBlock();\n  if (numBytes == 0) {\n    return empty;\n  }\n\n  // convert size in bytes to a number of pages\n  static const size_t pageSize = llvm::sys::Process::getPageSize().get();\n  const size_t numPages = (numBytes + pageSize - 1) / pageSize;\n\n  // try allocate memory\n  kern_return_t kr;\n  vm_size_t size = numPages * pageSize;\n  vm_address_t address = 0;\n\n  kr = vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);\n  QBDI_REQUIRE_ACTION(kr == KERN_SUCCESS, return empty);\n  // everything should be fine, set error code to 0\n  ec = std::error_code();\n\n  llvm::sys::MemoryBlock Result((void *)address, size);\n  // set permissions on memory range (if needed)\n  ec = llvm::sys::Memory::protectMappedMemory(Result, pFlags);\n\n  // flush instruction cache if we successfully allocated executable memory\n  if ((ec.value() == 0) && (pFlags & llvm::sys::Memory::MF_EXEC)) {\n    llvm::sys::Memory::InvalidateInstructionCache((const void *)address, size);\n  }\n\n  return Result;\n}\n\nvoid releaseMappedMemory(llvm::sys::MemoryBlock &block) {\n  vm_deallocate(mach_task_self(), (vm_address_t)block.base(),\n                block.allocatedSize());\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/AARCH64/ios-AARCH64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n.private_extern __qbdi_asmStackSwitch\n.align 4\n\n__qbdi_asmStackSwitch:\n\n    stp x29, x30, [sp, -16]!\n    mov x29, sp\n\n    and x1, x1, #~0xf\n    add x1, x1, #16\n    mov sp, x1\n        \n    mov x1, x29 \n        \n    blr x2\n\n    mov sp, x29\n    ldp x29, x30, [sp], 16\n\n    ret"
  },
  {
    "path": "src/Utility/AARCH64/ios-AARCH64e.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n.private_extern __qbdi_asmStackSwitch\n.align 4\n\n__qbdi_asmStackSwitch:\n\n    pacibsp\n    stp x29, x30, [sp, -16]!\n    mov x29, sp\n\n    and x1, x1, #~0xf\n    add x1, x1, #16\n    mov sp, x1\n        \n    mov x1, x29 \n        \n    blraaz x2\n\n    mov sp, x29\n    ldp x29, x30, [sp], 16\n\n    retab\n"
  },
  {
    "path": "src/Utility/AARCH64/linux-android-AARCH64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.hidden __qbdi_asmStackSwitch\n.globl  __qbdi_asmStackSwitch\n\n.align 8\n__qbdi_asmStackSwitch:\n  bti c;\n  stp x29, x30, [sp, #-16]!;\n  mov x29, sp;\n\n  bic\tx1, x1, #15;\n  mov sp, x1;\n  mov x1, x29;\n  blr x2;\n  mov sp, x29;\n  ldp x29, x30, [sp], 16;\n  ret;\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",@progbits\n"
  },
  {
    "path": "src/Utility/AARCH64/macos-AARCH64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.private_extern __qbdi_asmStackSwitch\n\n.align 8\n__qbdi_asmStackSwitch:\n  bti c;\n  stp x29, x30, [sp, #-16]!;\n  mov x29, sp;\n\n  bic\tx1, x1, #15;\n  mov sp, x1;\n  mov x1, x29;\n  blr x2;\n  mov sp, x29;\n  ldp x29, x30, [sp], 16;\n  ret;\n"
  },
  {
    "path": "src/Utility/AARCH64/macos-AARCH64e.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.private_extern __qbdi_asmStackSwitch\n\n.align 8\n__qbdi_asmStackSwitch:\n  bti c;\n  pacibsp;\n  stp x29, x30, [sp, #-16]!;\n  mov x29, sp;\n\n  bic\tx1, x1, #15;\n  mov sp, x1;\n  mov x1, x29;\n  blraaz x2;\n  mov sp, x29;\n  ldp x29, x30, [sp], 16;\n  retab;\n"
  },
  {
    "path": "src/Utility/ARM/CMakeLists.txt",
    "content": "set(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysis_ARM.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/linux-android_ARM.s\")\n\nelseif(QBDI_PLATFORM_IOS)\n  list(\n    APPEND\n    SOURCES\n    \"${CMAKE_CURRENT_LIST_DIR}/ios_ARM.asm\"\n    \"${CMAKE_CURRENT_LIST_DIR}/System_iOS.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/System_darwin.cpp\"\n    \"${CMAKE_CURRENT_LIST_DIR}/server-iOS-jit-user.c\")\n\nelse()\n  message(FATAL_ERROR \"No stub for ${QBDI_PLATFORM} (${QBDI_ARCH})\")\nendif()\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Utility/ARM/InstAnalysis_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <map>\n#include <string>\n\n#include \"Utils/ARMBaseInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"Patch/ARM/InstInfo_ARM.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/InstAnalysis_prive.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\nnamespace InstructionAnalysis {\n\nConditionType ConditionLLVM2QBDI(unsigned cond) {\n  switch (cond) {\n    case llvm::ARMCC::CondCodes::EQ:\n      return CONDITION_EQUALS;\n    case llvm::ARMCC::CondCodes::NE:\n      return CONDITION_NOT_EQUALS;\n    case llvm::ARMCC::CondCodes::HS:\n      return CONDITION_ABOVE_EQUALS;\n    case llvm::ARMCC::CondCodes::LO:\n      return CONDITION_BELOW;\n    case llvm::ARMCC::CondCodes::MI:\n      return CONDITION_SIGN;\n    case llvm::ARMCC::CondCodes::PL:\n      return CONDITION_NOT_SIGN;\n    case llvm::ARMCC::CondCodes::VS:\n      return CONDITION_OVERFLOW;\n    case llvm::ARMCC::CondCodes::VC:\n      return CONDITION_NOT_OVERFLOW;\n    case llvm::ARMCC::CondCodes::HI:\n      return CONDITION_ABOVE;\n    case llvm::ARMCC::CondCodes::LS:\n      return CONDITION_BELOW_EQUALS;\n    case llvm::ARMCC::CondCodes::GE:\n      return CONDITION_GREAT_EQUALS;\n    case llvm::ARMCC::CondCodes::LT:\n      return CONDITION_LESS;\n    case llvm::ARMCC::CondCodes::GT:\n      return CONDITION_GREAT;\n    case llvm::ARMCC::CondCodes::LE:\n      return CONDITION_LESS_EQUALS;\n    case llvm::ARMCC::CondCodes::AL:\n      return CONDITION_ALWAYS;\n    default:\n      QBDI_ABORT(\"Unsupported LLVM condition {}\", cond);\n  }\n}\n\nvoid analyseCondition(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                      const llvm::MCInstrDesc &desc, const LLVMCPU &llvmcpu) {\n\n  instAnalysis->condition = ConditionLLVM2QBDI(getCondition(inst, llvmcpu));\n\n  if (instAnalysis->condition == CONDITION_ALWAYS) {\n    instAnalysis->condition = CONDITION_NONE;\n  } else {\n    instAnalysis->flagsAccess |= REGISTER_READ;\n  }\n\n  for (unsigned int opn = 0; opn < desc.getNumOperands(); opn++) {\n    const llvm::MCOperandInfo &opInfo = desc.operands()[opn];\n    if (opInfo.RegClass == llvm::ARM::CCRRegClassID) {\n      const llvm::MCOperand &op = inst.getOperand(opn);\n      if (op.isReg() and op.getReg() == llvm::ARM::CPSR) {\n        instAnalysis->flagsAccess |= REGISTER_WRITE;\n      }\n      break;\n    }\n  }\n\n  // fix LLVM flag used/set\n  switch (inst.getOpcode()) {\n    default:\n      break;\n    case llvm::ARM::MRS:\n    case llvm::ARM::SEL:\n    case llvm::ARM::t2MRS_AR:\n    case llvm::ARM::t2SEL:\n      instAnalysis->flagsAccess |= REGISTER_READ;\n      break;\n    case llvm::ARM::MSR:\n    case llvm::ARM::MSRi:\n    case llvm::ARM::QADD:\n    case llvm::ARM::QDADD:\n    case llvm::ARM::QDSUB:\n    case llvm::ARM::QSUB:\n    case llvm::ARM::SADD16:\n    case llvm::ARM::SADD8:\n    case llvm::ARM::SASX:\n    case llvm::ARM::SMLABB:\n    case llvm::ARM::SMLABT:\n    case llvm::ARM::SMLAD:\n    case llvm::ARM::SMLADX:\n    case llvm::ARM::SMLATB:\n    case llvm::ARM::SMLATT:\n    case llvm::ARM::SMLAWB:\n    case llvm::ARM::SMLAWT:\n    case llvm::ARM::SMLSD:\n    case llvm::ARM::SMLSDX:\n    case llvm::ARM::SMUAD:\n    case llvm::ARM::SMUADX:\n    case llvm::ARM::SSAT16:\n    case llvm::ARM::SSAT:\n    case llvm::ARM::SSAX:\n    case llvm::ARM::SSUB16:\n    case llvm::ARM::SSUB8:\n    case llvm::ARM::UADD16:\n    case llvm::ARM::UADD8:\n    case llvm::ARM::UASX:\n    case llvm::ARM::USAT16:\n    case llvm::ARM::USAT:\n    case llvm::ARM::USAX:\n    case llvm::ARM::USUB16:\n    case llvm::ARM::USUB8:\n    case llvm::ARM::t2MSR_AR:\n    case llvm::ARM::t2QADD:\n    case llvm::ARM::t2QDADD:\n    case llvm::ARM::t2QDSUB:\n    case llvm::ARM::t2QSUB:\n    case llvm::ARM::t2SADD16:\n    case llvm::ARM::t2SADD8:\n    case llvm::ARM::t2SASX:\n    case llvm::ARM::t2SMLABB:\n    case llvm::ARM::t2SMLABT:\n    case llvm::ARM::t2SMLAD:\n    case llvm::ARM::t2SMLADX:\n    case llvm::ARM::t2SMLATB:\n    case llvm::ARM::t2SMLATT:\n    case llvm::ARM::t2SMLAWB:\n    case llvm::ARM::t2SMLAWT:\n    case llvm::ARM::t2SMLSD:\n    case llvm::ARM::t2SMLSDX:\n    case llvm::ARM::t2SMUAD:\n    case llvm::ARM::t2SMUADX:\n    case llvm::ARM::t2SSAT16:\n    case llvm::ARM::t2SSAT:\n    case llvm::ARM::t2SSAX:\n    case llvm::ARM::t2SSUB16:\n    case llvm::ARM::t2SSUB8:\n    case llvm::ARM::t2UADD16:\n    case llvm::ARM::t2UADD8:\n    case llvm::ARM::t2UASX:\n    case llvm::ARM::t2USAT16:\n    case llvm::ARM::t2USAT:\n    case llvm::ARM::t2USAX:\n    case llvm::ARM::t2USUB16:\n    case llvm::ARM::t2USUB8:\n      instAnalysis->flagsAccess |= REGISTER_WRITE;\n      break;\n  }\n}\n\nbool isFlagOperand(unsigned opcode, unsigned opNum,\n                   const llvm::MCOperandInfo &opdesc) {\n  if (opdesc.RegClass == llvm::ARM::CCRRegClassID)\n    return true;\n  return opdesc.isPredicate();\n}\n\nunsigned getBias(const llvm::MCInstrDesc &desc) {\n  unsigned NumDefs = desc.getNumDefs();\n  unsigned NumOps = desc.getNumOperands();\n  unsigned bias = 0;\n\n  for (unsigned opDef = 0; opDef < NumDefs; opDef++) {\n    bool found = false;\n    for (unsigned op = opDef + 1; op < NumOps; op++) {\n      if (desc.getOperandConstraint(op, llvm::MCOI::TIED_TO) == (int)opDef) {\n        bias = opDef + 1;\n        found = true;\n        break;\n      }\n    }\n    if (not found) {\n      break;\n    }\n  }\n\n  return bias;\n}\n\nunsigned getAdditionnalOperandNumber(const llvm::MCInst &inst,\n                                     const llvm::MCInstrDesc &desc) {\n\n  switch (inst.getOpcode()) {\n    case llvm::ARM::BX_RET:\n      return 1;\n    default:\n      return 0;\n  }\n}\n\nvoid getAdditionnalOperand(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                           const llvm::MCInstrDesc &desc,\n                           const llvm::MCRegisterInfo &MRI) {\n  switch (inst.getOpcode()) {\n    default:\n      break;\n    case llvm::ARM::BX_RET: {\n      // add LR\n      OperandAnalysis &opa2 = instAnalysis->operands[instAnalysis->numOperands];\n      analyseRegister(opa2, GPR_ID[REG_LR], MRI);\n      opa2.regAccess = REGISTER_READ;\n      opa2.flag |= OPERANDFLAG_IMPLICIT;\n      instAnalysis->numOperands++;\n      // try to merge with a previous one\n      tryMergeCurrentRegister(instAnalysis);\n      break;\n    }\n  }\n}\n\n} // namespace InstructionAnalysis\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/ARM/System_darwin.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <stdlib.h>\n\n#include <mach/host_info.h>\n#include <mach/mach.h>\n#include <mach/mach_host.h>\n\n#include \"llvm/ADT/StringMap.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/iterator.h\"\n#include \"llvm/TargetParser/Host.h\"\n#include \"llvm/TargetParser/SubtargetFeature.h\"\n\n#include \"QBDI/Config.h\"\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\nnamespace QBDI {\n\nstatic const std::string _getHostCPUName() {\n  host_basic_info_data_t hostInfo;\n  mach_msg_type_number_t infoCount;\n\n  infoCount = HOST_BASIC_INFO_COUNT;\n  host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo,\n            &infoCount);\n\n  // ARM32 kernel\n  if (hostInfo.cpu_type == CPU_TYPE_ARM) {\n    switch (hostInfo.cpu_subtype) {\n      case CPU_SUBTYPE_ARM_V4T:\n        return \"arm710t\";\n      case CPU_SUBTYPE_ARM_V6:\n        return \"arm1136j-s\";\n      case CPU_SUBTYPE_ARM_V5TEJ:\n        return \"arm9e\";\n      case CPU_SUBTYPE_ARM_XSCALE:\n        return \"xscale\";\n      case CPU_SUBTYPE_ARM_V7:\n        return \"cortex-a8\";\n      case CPU_SUBTYPE_ARM_V7F:\n        return \"cortex-a9\";\n      case CPU_SUBTYPE_ARM_V7S:\n        return \"swift\";\n      case CPU_SUBTYPE_ARM_V8:\n        return \"cortex-a53\";\n      default:\n        return \"arm710t\"; // Minimum model on iOS\n    }\n  }\n#ifdef CPU_TYPE_ARM64\n  // ARM64 kernel\n  else if (hostInfo.cpu_type == CPU_TYPE_ARM64) {\n#if defined(QBDI_ARCH_AARCH64)\n    switch (hostInfo.cpu_subtype) {\n      case CPU_SUBTYPE_ARM64_ALL:\n      case CPU_SUBTYPE_ARM64_V8:\n      default:\n        return \"cyclone\";\n      case CPU_SUBTYPE_ARM64E:\n        return \"apple-a12\";\n    }\n#endif\n    // armv7 / armv7s on ARM64 kernel\n    return \"swift\";\n  }\n#endif\n  QBDI_WARN(\"Unknown cpu type, using generic\");\n  return \"generic\";\n}\n\nconst std::string getHostCPUName() {\n  static std::once_flag cpuflag;\n  static std::string cpuname;\n\n  std::call_once(cpuflag, []() { cpuname = _getHostCPUName(); });\n\n  return cpuname;\n}\n\nconst std::vector<std::string> getHostCPUFeatures() {\n  std::vector<std::string> mattrs = {};\n  llvm::StringMap<bool> features = llvm::sys::getHostCPUFeatures();\n\n  bool ret = !features.empty();\n\n  if (!ret) {\n    features.clear();\n    const std::string cpuname = getHostCPUName();\n    if (cpuname == \"apple-a12\" or cpuname == \"cyclone\") {\n      features[\"fp-armv8\"] = true;\n      features[\"fullfp16\"] = true;\n      features[\"neon\"] = true;\n      if (cpuname == \"apple-a12\") {\n        features[\"v8.3a\"] = true;\n        features[\"pauth\"] = true;\n      }\n    } else {\n      QBDI_WARN(\"Fail to detect CPUHostFeatures\");\n    }\n  }\n\n  const char *fixupFeatures = getenv(\"QBDI_FIXUP_FEATURES\");\n  if (fixupFeatures != nullptr) {\n    llvm::SubtargetFeatures addFeatures(fixupFeatures);\n    for (const auto &f : addFeatures.getFeatures()) {\n      if (llvm::SubtargetFeatures::hasFlag(f)) {\n        features[llvm::SubtargetFeatures::StripFlag(f)] =\n            llvm::SubtargetFeatures::isEnabled(f);\n      } else {\n        features[f] = true;\n      }\n    }\n  }\n\n  if (ret || fixupFeatures != nullptr) {\n\n    for (const auto &feat : features) {\n      QBDI_DEBUG(\"Feature {}: {}\", feat.getKeyData(), feat.getValue());\n      if (!feat.getValue()) {\n        continue;\n      }\n      mattrs.push_back(feat.getKey().str());\n    }\n  }\n\n  if constexpr (is_arm)\n    // set default ARM CPU\n    if (features.size() == 0) {\n      features.insert({\"fp16\", true});\n      features.insert({\"d16\", true});\n    }\n  // Fixing awfull LLVM API\n  if (features.count(\"fp16\") && features[\"fp16\"]) {\n    mattrs.emplace_back(\"vfp2\");\n  }\n  if (features.count(\"d16\") && features[\"d16\"]) {\n    mattrs.emplace_back(\"vfp3\");\n  }\n\n  return mattrs;\n}\n\nbool isHostCPUFeaturePresent(const char *query) {\n  std::vector<std::string> features = getHostCPUFeatures();\n  for (const std::string &feature : features) {\n    if (feature == query) {\n      return true;\n    }\n  }\n  return false;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/ARM/System_iOS.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Platform.h\"\n\n#include <mach/host_info.h>\n#include <mach/mach.h>\n#include <mach/mach_host.h>\n#include <mach/machine.h>\n#include <mach/vm_map.h>\n#include <mutex>\n\n#include \"llvm/Support/Process.h\"\n#include \"llvm/TargetParser/Host.h\"\n\n#include \"Utility/AARCH64/server-iOS-jit-user.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#ifndef PT_TRACE_ME\n#define PT_TRACE_ME 0\n#define PT_DETACH 11\n#endif\n\nstatic mach_port_t frida_jit = MACH_PORT_NULL;\n\nextern \"C\" {\nkern_return_t bootstrap_look_up(mach_port_t bp, const char *service_name,\n                                mach_port_t *sp);\nint ptrace(int request, pid_t pid, caddr_t addr, int data);\n}\n\nnamespace QBDI {\n\nvoid init_jit_server() {\n  // connect to frida JIT server\n  if (frida_jit == MACH_PORT_NULL) {\n    bootstrap_look_up(bootstrap_port, \"com.apple.uikit.viewservice.frida\",\n                      &frida_jit);\n    if (frida_jit == MACH_PORT_NULL) {\n      QBDI_ERROR(\"Cannot attach to Frida JIT server !\");\n    }\n  }\n  // disable code signature enforcement\n  ptrace(PT_TRACE_ME, 0, 0, 0);\n  ptrace(PT_DETACH, 0, 0, 0);\n}\n\nvoid terminate_jit_server() {\n  if (frida_jit != MACH_PORT_NULL) {\n    mach_port_deallocate(mach_task_self(), frida_jit);\n    frida_jit = MACH_PORT_NULL;\n  }\n}\n\nstd::once_flag rwxflag;\nusing PF = llvm::sys::Memory::ProtectionFlags;\n\nbool isRWXSupported() {\n  static bool cached_result = false;\n\n  std::call_once(rwxflag, []() {\n    bool supported = false;\n    bool success = false;\n    mach_port_t task;\n    vm_address_t page = 0;\n    vm_address_t address;\n    vm_size_t size = 0;\n    natural_t depth = 0;\n    struct vm_region_submap_info_64 info;\n    mach_msg_type_number_t info_count;\n    kern_return_t kr;\n    size_t pageSize = llvm::sys::Process::getPageSize();\n\n    task = mach_task_self();\n\n    kr = vm_allocate(task, &page, pageSize, VM_FLAGS_ANYWHERE);\n    QBDI_REQUIRE_ACTION(kr == KERN_SUCCESS, return);\n\n    llvm::sys::MemoryBlock block((void *)page, pageSize);\n\n    std::error_code ec = llvm::sys::Memory::protectMappedMemory(\n        block, PF::MF_READ | PF::MF_WRITE | PF::MF_EXEC);\n    success = ec.value() == 0;\n\n    address = page;\n    while (success) {\n      info_count = VM_REGION_SUBMAP_INFO_COUNT_64;\n      kr = vm_region_recurse_64(task, &address, &size, &depth,\n                                (vm_region_recurse_info_t)&info, &info_count);\n      if (kr != KERN_SUCCESS)\n        break;\n\n      if (info.is_submap) {\n        depth++;\n        continue;\n      } else {\n        vm_prot_t requested_prot =\n            VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;\n        supported = (info.protection & requested_prot) == requested_prot;\n        break;\n      }\n    }\n\n    vm_deallocate(task, page, pageSize);\n\n    cached_result = supported;\n  });\n\n  return cached_result;\n}\n\nllvm::sys::MemoryBlock\nallocateMappedMemory(size_t numBytes,\n                     const llvm::sys::MemoryBlock *const nearBlock,\n                     unsigned pFlags, std::error_code &ec) {\n  // Set default error code\n  ec = std::error_code(ENOMEM, std::system_category());\n  // create an empty memory block (in case of failure)\n  llvm::sys::MemoryBlock empty = llvm::sys::MemoryBlock();\n  if (numBytes == 0) {\n    return empty;\n  }\n\n  // convert size in bytes to a number of pages\n  static const size_t pageSize = llvm::sys::Process::getPageSize();\n  const size_t numPages = (numBytes + pageSize - 1) / pageSize;\n\n  // try allocate memory\n  kern_return_t kr;\n  vm_size_t size = numPages * pageSize;\n  vm_address_t address = 0;\n  mach_vm_address_t page =\n      0; // careful here to use correct type as size can be different\n  bool setPerms = true;\n\n  // we want executable memory and our process can't have it...\n  // try to use a JIT server (Frida one by default)\n  if ((pFlags & llvm::sys::Memory::MF_EXEC) && !isRWXSupported()) {\n    // lazily init JIT server\n    if (frida_jit == MACH_PORT_NULL) {\n      init_jit_server();\n    }\n    QBDI_REQUIRE_ACTION(frida_jit != MACH_PORT_NULL, return empty);\n    kr = frida_jit_alloc(frida_jit, mach_task_self(), &page, size,\n                         VM_FLAGS_ANYWHERE);\n    address = page;\n    // no need to set permissions later\n    setPerms = false;\n  } else {\n    kr = vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);\n  }\n  QBDI_REQUIRE_ACTION(kr == KERN_SUCCESS, return empty);\n  // everything should be fine, set error code to 0\n  ec = std::error_code();\n\n  llvm::sys::MemoryBlock Result((void *)address, size);\n  // set permissions on memory range (if needed)\n  if (setPerms) {\n    ec = llvm::sys::Memory::protectMappedMemory(Result, pFlags);\n  }\n\n  // flush instruction cache if we successfully allocated executable memory\n  if ((ec.value() == 0) && (pFlags & llvm::sys::Memory::MF_EXEC)) {\n    llvm::sys::Memory::InvalidateInstructionCache((const void *)address, size);\n  }\n\n  return Result;\n}\n\nvoid releaseMappedMemory(llvm::sys::MemoryBlock &block) {\n  vm_deallocate(mach_task_self(), (vm_address_t)block.base(), block.size());\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/ARM/ios_ARM.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n.code 32\n.section .text\n.align 4\n.hidden __qbdi_asmStackSwitch\n.globl  __qbdi_asmStackSwitch\n.type __qbdi_asmStackSwitch, %function\n\n__qbdi_asmStackSwitch:\n    push {fp, lr};\n    mov fp, sp;\n\n    bic\tr1, r1, #15;\n    add r1, r1, #12;\n    mov sp, r1;\n\n    push {r3};\n    mov r1, fp;\n    blx r2;\n\n    mov sp, fp;\n    pop {fp, pc};\n"
  },
  {
    "path": "src/Utility/ARM/linux-android_ARM.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.text\n\n;#if defined(__ARM_PCS_VFP)\n;.fpu vfp\n.fpu neon\n\n;#endif\n\n.code 32\n.section .text\n.align 4\n.hidden __qbdi_asmStackSwitch\n.globl  __qbdi_asmStackSwitch\n.type __qbdi_asmStackSwitch, %function\n\n__qbdi_asmStackSwitch:\n    push {fp, lr};\n    mov fp, sp;\n\n    bic\tr1, r1, #15;\n    add r1, r1, #12;\n    mov sp, r1;\n\n    push {r3};\n    mov r1, fp;\n    blx r2;\n\n    mov sp, fp;\n    pop {fp, pc};\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",%progbits\n"
  },
  {
    "path": "src/Utility/ARM/server-iOS-jit-user.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*\n * IDENTIFICATION:\n * stub generated Tue Sep 26 10:48:47 2017\n * with a MiG generated by bootstrap_cmds-96.20.2\n * OPTIONS:\n */\n#define __MIG_check__Reply__frida_jit_subsystem__ 1\n\n#include \"server-iOS-jit-user.h\"\n\n#ifndef mig_internal\n#define mig_internal static __inline__\n#endif /* mig_internal */\n\n#ifndef mig_external\n#define mig_external\n#endif /* mig_external */\n\n#if !defined(__MigTypeCheck) && defined(TypeCheck)\n#define __MigTypeCheck TypeCheck /* Legacy setting */\n#endif                           /* !defined(__MigTypeCheck) */\n\n#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)\n#define __MigKernelSpecificCode                \\\n  _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting \\\n                              */\n#endif                       /* !defined(__MigKernelSpecificCode) */\n\n#ifndef LimitCheck\n#define LimitCheck 0\n#endif /* LimitCheck */\n\n#ifndef min\n#define min(a, b) (((a) < (b)) ? (a) : (b))\n#endif /* min */\n\n#if !defined(_WALIGN_)\n#define _WALIGN_(x) (((x) + 3) & ~3)\n#endif /* !defined(_WALIGN_) */\n\n#if !defined(_WALIGNSZ_)\n#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))\n#endif /* !defined(_WALIGNSZ_) */\n\n#ifndef UseStaticTemplates\n#define UseStaticTemplates 0\n#endif /* UseStaticTemplates */\n\n#ifndef __MachMsgErrorWithTimeout\n#define __MachMsgErrorWithTimeout(_R_)                     \\\n  {                                                        \\\n    switch (_R_) {                                         \\\n      case MACH_SEND_INVALID_DATA:                         \\\n      case MACH_SEND_INVALID_DEST:                         \\\n      case MACH_SEND_INVALID_HEADER:                       \\\n        mig_put_reply_port(InP->Head.msgh_reply_port);     \\\n        break;                                             \\\n      case MACH_SEND_TIMED_OUT:                            \\\n      case MACH_RCV_TIMED_OUT:                             \\\n      default:                                             \\\n        mig_dealloc_reply_port(InP->Head.msgh_reply_port); \\\n    }                                                      \\\n  }\n#endif /* __MachMsgErrorWithTimeout */\n\n#ifndef __MachMsgErrorWithoutTimeout\n#define __MachMsgErrorWithoutTimeout(_R_)                  \\\n  {                                                        \\\n    switch (_R_) {                                         \\\n      case MACH_SEND_INVALID_DATA:                         \\\n      case MACH_SEND_INVALID_DEST:                         \\\n      case MACH_SEND_INVALID_HEADER:                       \\\n        mig_put_reply_port(InP->Head.msgh_reply_port);     \\\n        break;                                             \\\n      default:                                             \\\n        mig_dealloc_reply_port(InP->Head.msgh_reply_port); \\\n    }                                                      \\\n  }\n#endif /* __MachMsgErrorWithoutTimeout */\n\n#ifndef __DeclareSendRpc\n#define __DeclareSendRpc(_NUM_, _NAME_)\n#endif /* __DeclareSendRpc */\n\n#ifndef __BeforeSendRpc\n#define __BeforeSendRpc(_NUM_, _NAME_)\n#endif /* __BeforeSendRpc */\n\n#ifndef __AfterSendRpc\n#define __AfterSendRpc(_NUM_, _NAME_)\n#endif /* __AfterSendRpc */\n\n#ifndef __DeclareSendSimple\n#define __DeclareSendSimple(_NUM_, _NAME_)\n#endif /* __DeclareSendSimple */\n\n#ifndef __BeforeSendSimple\n#define __BeforeSendSimple(_NUM_, _NAME_)\n#endif /* __BeforeSendSimple */\n\n#ifndef __AfterSendSimple\n#define __AfterSendSimple(_NUM_, _NAME_)\n#endif /* __AfterSendSimple */\n\n#define msgh_request_port msgh_remote_port\n#define msgh_reply_port msgh_local_port\n\n#if (__MigTypeCheck)\n#if __MIG_check__Reply__frida_jit_subsystem__\n#if !defined(__MIG_check__Reply__frida_jit_alloc_t__defined)\n#define __MIG_check__Reply__frida_jit_alloc_t__defined\n\nmig_internal kern_return_t\n__MIG_check__Reply__frida_jit_alloc_t(__Reply__frida_jit_alloc_t *Out0P) {\n\n  typedef __Reply__frida_jit_alloc_t __Reply __attribute__((unused));\n#if __MigTypeCheck\n  unsigned int msgh_size;\n#endif /* __MigTypeCheck */\n  if (Out0P->Head.msgh_id != 421437) {\n    if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE) {\n      return MIG_SERVER_DIED;\n    } else {\n      return MIG_REPLY_MISMATCH;\n    }\n  }\n\n#if __MigTypeCheck\n  msgh_size = Out0P->Head.msgh_size;\n\n  if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||\n      ((msgh_size != (mach_msg_size_t)sizeof(__Reply)) &&\n       (msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||\n        Out0P->RetCode == KERN_SUCCESS))) {\n    return MIG_TYPE_ERROR;\n  }\n#endif /* __MigTypeCheck */\n\n  if (Out0P->RetCode != KERN_SUCCESS) {\n    return ((mig_reply_error_t *)Out0P)->RetCode;\n  }\n\n  return MACH_MSG_SUCCESS;\n}\n#endif /* !defined(__MIG_check__Reply__frida_jit_alloc_t__defined) */\n#endif /* __MIG_check__Reply__frida_jit_subsystem__ */\n#endif /* ( __MigTypeCheck ) */\n\n/* Routine frida_jit_alloc */\nmig_external kern_return_t frida_jit_alloc(mach_port_t server, vm_map_t task,\n                                           mach_vm_address_t *address,\n                                           mach_vm_size_t size, int flags) {\n\n#ifdef __MigPackStructs\n#pragma pack(4)\n#endif\n  typedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_port_descriptor_t task;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    int flags;\n  } Request __attribute__((unused));\n#ifdef __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef __MigPackStructs\n#pragma pack(4)\n#endif\n  typedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_address_t address;\n    mach_msg_trailer_t trailer;\n  } Reply __attribute__((unused));\n#ifdef __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef __MigPackStructs\n#pragma pack(4)\n#endif\n  typedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_address_t address;\n  } __Reply __attribute__((unused));\n#ifdef __MigPackStructs\n#pragma pack()\n#endif\n  /*\n   * typedef struct {\n   * \tmach_msg_header_t Head;\n   * \tNDR_record_t NDR;\n   * \tkern_return_t RetCode;\n   * } mig_reply_error_t;\n   */\n\n  union {\n    Request In;\n    Reply Out;\n  } Mess;\n\n  Request *InP = &Mess.In;\n  Reply *Out0P = &Mess.Out;\n\n  mach_msg_return_t msg_result;\n\n#ifdef __MIG_check__Reply__frida_jit_alloc_t__defined\n  kern_return_t check_result;\n#endif /* __MIG_check__Reply__frida_jit_alloc_t__defined */\n\n  __DeclareSendRpc(421337, \"frida_jit_alloc\")\n\n#if UseStaticTemplates\n      const static mach_msg_port_descriptor_t taskTemplate = {\n          /* name = */ MACH_PORT_NULL,\n          /* pad1 = */ 0,\n          /* pad2 = */ 0,\n          /* disp = */ 19,\n          /* type = */ MACH_MSG_PORT_DESCRIPTOR,\n      };\n#endif /* UseStaticTemplates */\n\n  InP->msgh_body.msgh_descriptor_count = 1;\n#if UseStaticTemplates\n  InP->task = taskTemplate;\n  InP->task.name = task;\n#else  /* UseStaticTemplates */\n  InP->task.name = task;\n  InP->task.disposition = 19;\n  InP->task.type = MACH_MSG_PORT_DESCRIPTOR;\n#endif /* UseStaticTemplates */\n\n  InP->NDR = NDR_record;\n\n  InP->address = *address;\n\n  InP->size = size;\n\n  InP->flags = flags;\n\n  InP->Head.msgh_bits =\n      MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);\n  /* msgh_size passed as argument */\n  InP->Head.msgh_request_port = server;\n  InP->Head.msgh_reply_port = mig_get_reply_port();\n  InP->Head.msgh_id = 421337;\n  InP->Head.msgh_reserved = 0;\n\n  /* BEGIN VOUCHER CODE */\n\n#ifdef USING_VOUCHERS\n  if (voucher_mach_msg_set != NULL) {\n    voucher_mach_msg_set(&InP->Head);\n  }\n#endif // USING_VOUCHERS\n\n  /* END VOUCHER CODE */\n\n  __BeforeSendRpc(421337, \"frida_jit_alloc\") msg_result = mach_msg(\n      &InP->Head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_MSG_OPTION_NONE,\n      (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply),\n      InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n  __AfterSendRpc(421337,\n                 \"frida_jit_alloc\") if (msg_result != MACH_MSG_SUCCESS) {\n    __MachMsgErrorWithoutTimeout(msg_result);\n    {\n      return msg_result;\n    }\n  }\n\n#if defined(__MIG_check__Reply__frida_jit_alloc_t__defined)\n  check_result = __MIG_check__Reply__frida_jit_alloc_t(\n      (__Reply__frida_jit_alloc_t *)Out0P);\n  if (check_result != MACH_MSG_SUCCESS) {\n    return check_result;\n  }\n#endif /* defined(__MIG_check__Reply__frida_jit_alloc_t__defined) */\n\n  *address = Out0P->address;\n\n  return KERN_SUCCESS;\n}\n"
  },
  {
    "path": "src/Utility/ARM/server-iOS-jit-user.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _frida_jit_user_\n#define _frida_jit_user_\n\n/* Module frida_jit */\n\n#include <mach/boolean.h>\n#include <mach/kern_return.h>\n#include <mach/mach_types.h>\n#include <mach/message.h>\n#include <mach/mig_errors.h>\n#include <mach/ndr.h>\n#include <mach/notify.h>\n#include <mach/port.h>\n#include <string.h>\n\n/* BEGIN VOUCHER CODE */\n\n#ifndef KERNEL\n#if defined(__has_include)\n#if __has_include(<mach/mig_voucher_support.h>)\n#ifndef USING_VOUCHERS\n#define USING_VOUCHERS\n#endif\n#ifndef __VOUCHER_FORWARD_TYPE_DECLS__\n#define __VOUCHER_FORWARD_TYPE_DECLS__\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nextern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg)\n    __attribute__((weak_import));\n#ifdef __cplusplus\n}\n#endif\n#endif // __VOUCHER_FORWARD_TYPE_DECLS__\n#endif // __has_include(<mach/mach_voucher_types.h>)\n#endif // __has_include\n#endif // !KERNEL\n\n/* END VOUCHER CODE */\n\n/* BEGIN MIG_STRNCPY_ZEROFILL CODE */\n\n#if defined(__has_include)\n#if __has_include(<mach/mig_strncpy_zerofill_support.h>)\n#ifndef USING_MIG_STRNCPY_ZEROFILL\n#define USING_MIG_STRNCPY_ZEROFILL\n#endif\n#ifndef __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__\n#define __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nextern int mig_strncpy_zerofill(char *dest, const char *src, int len)\n    __attribute__((weak_import));\n#ifdef __cplusplus\n}\n#endif\n#endif /* __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ */\n#endif /* __has_include(<mach/mig_strncpy_zerofill_support.h>) */\n#endif /* __has_include */\n\n/* END MIG_STRNCPY_ZEROFILL CODE */\n\n#ifdef AUTOTEST\n#ifndef FUNCTION_PTR_T\n#define FUNCTION_PTR_T\ntypedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);\ntypedef struct {\n  char *name;\n  function_ptr_t function;\n} function_table_entry;\ntypedef function_table_entry *function_table_t;\n#endif /* FUNCTION_PTR_T */\n#endif /* AUTOTEST */\n\n#ifndef frida_jit_MSG_COUNT\n#define frida_jit_MSG_COUNT 1\n#endif /* frida_jit_MSG_COUNT */\n\n#include <mach/mach_types.h>\n#include <mach/mig.h>\n#include <mach/std_types.h>\n\n#ifdef __BeforeMigUserHeader\n__BeforeMigUserHeader\n#endif /* __BeforeMigUserHeader */\n\n#include <sys/cdefs.h>\n    __BEGIN_DECLS\n\n/* Routine frida_jit_alloc */\n#ifdef mig_external\n        mig_external\n#else\nextern\n#endif /* mig_external */\n            kern_return_t\n            frida_jit_alloc(mach_port_t server, vm_map_t task,\n                            mach_vm_address_t *address, mach_vm_size_t size,\n                            int flags);\n\n__END_DECLS\n\n/********************** Caution **************************/\n/* The following data types should be used to calculate  */\n/* maximum message sizes only. The actual message may be */\n/* smaller, and the position of the arguments within the */\n/* message layout may vary from what is presented here.  */\n/* For example, if any of the arguments are variable-    */\n/* sized, and less than the maximum is sent, the data    */\n/* will be packed tight in the actual message to reduce  */\n/* the presence of holes.                                */\n/********************** Caution **************************/\n\n/* typedefs for all requests */\n\n#ifndef __Request__frida_jit_subsystem__defined\n#define __Request__frida_jit_subsystem__defined\n\n#ifdef __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n  mach_msg_header_t Head;\n  /* start of the kernel processed data */\n  mach_msg_body_t msgh_body;\n  mach_msg_port_descriptor_t task;\n  /* end of the kernel processed data */\n  NDR_record_t NDR;\n  mach_vm_address_t address;\n  mach_vm_size_t size;\n  int flags;\n} __Request__frida_jit_alloc_t __attribute__((unused));\n#ifdef __MigPackStructs\n#pragma pack()\n#endif\n#endif /* !__Request__frida_jit_subsystem__defined */\n\n/* union of all requests */\n\n#ifndef __RequestUnion__frida_jit_subsystem__defined\n#define __RequestUnion__frida_jit_subsystem__defined\nunion __RequestUnion__frida_jit_subsystem {\n  __Request__frida_jit_alloc_t Request_frida_jit_alloc;\n};\n#endif /* !__RequestUnion__frida_jit_subsystem__defined */\n/* typedefs for all replies */\n\n#ifndef __Reply__frida_jit_subsystem__defined\n#define __Reply__frida_jit_subsystem__defined\n\n#ifdef __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n  mach_msg_header_t Head;\n  NDR_record_t NDR;\n  kern_return_t RetCode;\n  mach_vm_address_t address;\n} __Reply__frida_jit_alloc_t __attribute__((unused));\n#ifdef __MigPackStructs\n#pragma pack()\n#endif\n#endif /* !__Reply__frida_jit_subsystem__defined */\n\n/* union of all replies */\n\n#ifndef __ReplyUnion__frida_jit_subsystem__defined\n#define __ReplyUnion__frida_jit_subsystem__defined\nunion __ReplyUnion__frida_jit_subsystem {\n  __Reply__frida_jit_alloc_t Reply_frida_jit_alloc;\n};\n#endif /* !__RequestUnion__frida_jit_subsystem__defined */\n\n#ifndef subsystem_to_name_map_frida_jit\n#define subsystem_to_name_map_frida_jit {\"frida_jit_alloc\", 421337}\n#endif\n\n#ifdef __AfterMigUserHeader\n__AfterMigUserHeader\n#endif /* __AfterMigUserHeader */\n\n#endif /* _frida_jit_user_ */\n"
  },
  {
    "path": "src/Utility/CMakeLists.txt",
    "content": "# Add QBDI target\ntarget_sources(\n  QBDI_src\n  INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysis.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/LogSys.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/Memory.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/StackSwitch.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/String.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/Version.cpp\")\n\nif(QBDI_PLATFORM_ANDROID OR QBDI_PLATFORM_LINUX)\n  target_sources(\n    QBDI_src INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/Memory_linux.cpp\"\n                       \"${CMAKE_CURRENT_LIST_DIR}/System_generic.cpp\")\nelseif(QBDI_PLATFORM_MACOS OR QBDI_PLATFORM_IOS)\n  target_sources(QBDI_src\n                 INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/Memory_macos.cpp\")\n  if(NOT QBDI_ARCH_AARCH64)\n    target_sources(QBDI_src\n                   INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/System_generic.cpp\")\n  endif()\nelseif(QBDI_PLATFORM_WINDOWS)\n  target_sources(\n    QBDI_src INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/Memory_windows.cpp\"\n                       \"${CMAKE_CURRENT_LIST_DIR}/System_generic.cpp\")\nendif()\n\nif(QBDI_ARCH_X86 OR QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nendif()\n"
  },
  {
    "path": "src/Utility/InstAnalysis.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <bitset>\n#include <cstdint>\n#include <map>\n#include <string.h>\n#include <string>\n#include <utility>\n\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n#include \"llvm/MC/MCRegister.h\"\n#include \"llvm/MC/MCRegisterInfo.h\"\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/InstInfo.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n#include \"Utility/InstAnalysis_prive.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/State.h\"\n\n#ifndef QBDI_PLATFORM_WINDOWS\n#if defined(QBDI_PLATFORM_LINUX) && !defined(__USE_GNU)\n#define __USE_GNU\n#endif\n#include <dlfcn.h>\n#endif\n\nnamespace QBDI {\nnamespace InstructionAnalysis {\n\nstatic bool isFlagRegister(RegLLVM regNo) {\n  if (regNo == /* llvm::X86|ARM::NoRegister */ 0) {\n    return false;\n  }\n  if (GPR_ID[REG_FLAG] == regNo) {\n    return true;\n  }\n  for (size_t j = 0; j < size_FLAG_ID; j++) {\n    if (regNo == FLAG_ID[j]) {\n      return true;\n    }\n  }\n  return false;\n}\n\nvoid analyseRegister(OperandAnalysis &opa, RegLLVM regNo,\n                     const llvm::MCRegisterInfo &MRI) {\n  opa.regName = MRI.getName(regNo.getValue());\n  if (opa.regName != nullptr && opa.regName[0] == '\\x00') {\n    opa.regName = nullptr;\n  }\n  opa.value = regNo.getValue();\n  opa.size = 0;\n  opa.flag = OPERANDFLAG_NONE;\n  opa.regOff = 0;\n  opa.regCtxIdx = -1;\n  opa.type = OPERAND_INVALID;\n  opa.regAccess = REGISTER_UNUSED;\n  if (regNo == /* llvm::X86|ARM::NoRegister */ 0) {\n    return;\n  }\n  // try to match register in our GPR context\n  size_t gprIndex = getGPRPosition(regNo);\n  if (gprIndex != ((size_t)-1)) {\n    if (MRI.isSubRegisterEq(GPR_ID[gprIndex].getValue(), regNo.getValue())) {\n      if (GPR_ID[gprIndex] != regNo) {\n        opa.regOff = getRegisterBaseOffset(regNo);\n      }\n      opa.regCtxIdx = gprIndex;\n      opa.size = getRegisterSize(regNo);\n      opa.type = OPERAND_GPR;\n      if (!opa.size) {\n        QBDI_WARN(\"register {} ({}) with size null\", regNo.getValue(),\n                  opa.regName);\n      }\n      return;\n    }\n    if (getRegisterPacked(regNo) <= 1) {\n      QBDI_WARN(\"register {} ({}) has index {} but isn't a subregister\",\n                regNo.getValue(), opa.regName, gprIndex);\n    }\n  }\n  // try to match register in our FPR context\n  auto it = FPR_ID.find(regNo);\n  if (it != FPR_ID.end()) {\n    opa.regCtxIdx = it->second;\n    opa.regOff = 0;\n    opa.size = getRegisterSize(regNo);\n    opa.type = OPERAND_FPR;\n    if (!opa.size) {\n      QBDI_WARN(\"register {} ({}) with size null\", regNo.getValue(),\n                opa.regName);\n    }\n    return;\n  }\n  for (uint16_t j = 0; j < size_SEG_ID; j++) {\n    if (regNo == SEG_ID[j]) {\n      opa.regCtxIdx = -1;\n      opa.regOff = 0;\n      opa.size = getRegisterSize(regNo);\n      opa.type = OPERAND_SEG;\n      if (!opa.size) {\n        QBDI_WARN(\"register {} ({}) with size null\", regNo.getValue(),\n                  opa.regName);\n      }\n      return;\n    }\n  }\n  if (getRegisterPacked(regNo) > 1) {\n    // multiple register, the operand analyse is handled by the target\n    opa.regCtxIdx = -1;\n    opa.regOff = 0;\n    opa.size = 0;\n    opa.type = OPERAND_INVALID;\n  } else {\n    // unsupported register\n    QBDI_WARN(\"Unknown register {} : {}\", regNo.getValue(), opa.regName);\n    opa.regCtxIdx = -1;\n    opa.regOff = 0;\n    opa.size = 0;\n    opa.type = OPERAND_SEG;\n  }\n  return;\n}\n\nvoid breakPackedRegister(InstAnalysis *instAnalysis, OperandAnalysis &opa,\n                         RegLLVM regNo, const llvm::MCRegisterInfo &MRI) {\n\n  size_t gprIndex = getGPRPosition(regNo);\n\n  // packed GPR\n  if (gprIndex != (size_t)-1) {\n    opa.regCtxIdx = gprIndex;\n    opa.size = getRegisterSize(regNo);\n    opa.type = OPERAND_GPR;\n    opa.regName = MRI.getName(getPackedRegister(regNo, 0).getValue());\n    if (opa.regName != nullptr && opa.regName[0] == '\\x00') {\n      opa.regName = nullptr;\n    }\n\n    for (unsigned p = 1; p < getRegisterPacked(regNo); p++) {\n      OperandAnalysis &opa2 = instAnalysis->operands[instAnalysis->numOperands];\n      memcpy(&opa2, &opa, sizeof(OperandAnalysis));\n      opa2.regCtxIdx = getGPRPosition(getPackedRegister(regNo, p));\n      opa2.regName = MRI.getName(getPackedRegister(regNo, p).getValue());\n      if (opa2.regName != nullptr && opa2.regName[0] == '\\x00') {\n        opa2.regName = nullptr;\n      }\n      QBDI_REQUIRE(opa2.regCtxIdx != -1);\n      instAnalysis->numOperands++;\n    }\n    return;\n  }\n  // packed FPR\n  auto it = FPR_ID.find(getPackedRegister(regNo, 0));\n  if (it != FPR_ID.end()) {\n    opa.regCtxIdx = it->second;\n    opa.size = getRegisterSize(regNo);\n    opa.type = OPERAND_FPR;\n    opa.regName = MRI.getName(getPackedRegister(regNo, 0).getValue());\n    if (opa.regName != nullptr && opa.regName[0] == '\\x00') {\n      opa.regName = nullptr;\n    }\n    for (unsigned p = 1; p < getRegisterPacked(regNo); p++) {\n      OperandAnalysis &opa2 = instAnalysis->operands[instAnalysis->numOperands];\n      memcpy(&opa2, &opa, sizeof(OperandAnalysis));\n      auto it2 = FPR_ID.find(getPackedRegister(regNo, p));\n      if (it2 != FPR_ID.end()) {\n        opa2.regCtxIdx = it2->second;\n      } else {\n        opa2.regCtxIdx = -1;\n      }\n      opa2.regName = MRI.getName(getPackedRegister(regNo, p).getValue());\n      if (opa2.regName != nullptr && opa2.regName[0] == '\\x00') {\n        opa2.regName = nullptr;\n      }\n      instAnalysis->numOperands++;\n    }\n    return;\n  }\n  // unsupported Packed Register\n  QBDI_WARN(\"Unknown register {} : {}\", regNo.getValue(), opa.regName);\n  opa.type = OPERAND_SEG;\n  return;\n}\n\nvoid tryMergeCurrentRegister(InstAnalysis *instAnalysis) {\n  OperandAnalysis &opa = instAnalysis->operands[instAnalysis->numOperands - 1];\n  if ((opa.type != QBDI::OPERAND_GPR && opa.type != QBDI::OPERAND_FPR) ||\n      opa.regCtxIdx < 0) {\n    return;\n  }\n  if ((opa.flag & QBDI::OPERANDFLAG_IMPLICIT) == 0) {\n    return;\n  }\n  for (uint16_t j = 0; j < instAnalysis->numOperands - 1; j++) {\n    OperandAnalysis &pop = instAnalysis->operands[j];\n    if (pop.type != opa.type || pop.flag != opa.flag) {\n      continue;\n    }\n    if (pop.regCtxIdx == opa.regCtxIdx && pop.size == opa.size &&\n        pop.regOff == opa.regOff) {\n      // merge current one into previous one\n      pop.regAccess |= opa.regAccess;\n      memset(&opa, 0, sizeof(OperandAnalysis));\n      instAnalysis->numOperands--;\n      break;\n    }\n  }\n}\n\nvoid analyseImplicitRegisters(InstAnalysis *instAnalysis,\n                              llvm::ArrayRef<llvm::MCPhysReg> implicitRegs,\n                              RegisterAccessType type,\n                              const llvm::MCRegisterInfo &MRI) {\n  // Iteration style copied from LLVM code\n  for (const llvm::MCPhysReg &regNo : implicitRegs) {\n    if (isFlagRegister(regNo)) {\n      instAnalysis->flagsAccess |= type;\n      continue;\n    }\n    OperandAnalysis topa;\n    analyseRegister(topa, regNo, MRI);\n    // we found a GPR (as size is only known for GPR)\n    // TODO: add support for more registers\n    if (topa.size != 0 && topa.type != OPERAND_INVALID) {\n      // fill a new operand analysis\n      OperandAnalysis &opa = instAnalysis->operands[instAnalysis->numOperands];\n      opa = topa;\n      opa.regAccess = type;\n      opa.flag |= OPERANDFLAG_IMPLICIT;\n      instAnalysis->numOperands++;\n      // try to merge with a previous one\n      tryMergeCurrentRegister(instAnalysis);\n    }\n  }\n}\n\nvoid analyseOperands(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                     const LLVMCPU &llvmcpu) {\n  if (!instAnalysis) {\n    // no instruction analysis\n    return;\n  }\n  const llvm::MCInstrDesc &desc = llvmcpu.getMCII().get(inst.getOpcode());\n  const llvm::MCRegisterInfo &MRI = llvmcpu.getMRI();\n\n  instAnalysis->numOperands = 0; // updated later because we could skip some\n  instAnalysis->operands = NULL;\n  // number of first def operand that are tied to a later used operand\n  unsigned operandBias = getBias(desc);\n  // Analysis of instruction operands\n  unsigned numOperands = inst.getNumOperands();\n  unsigned numOperandsMax =\n      numOperands + desc.NumImplicitDefs + desc.NumImplicitUses - operandBias;\n  // (R|E)SP are missing for RET and CALL in x86\n  numOperandsMax += getAdditionnalOperandNumber(inst, desc);\n  // packedRegister\n  for (unsigned i = 0; i < inst.getNumOperands(); i++) {\n    const llvm::MCOperand &op = inst.getOperand(i);\n    if (op.isReg() and getRegisterPacked(op.getReg()) > 1) {\n      numOperandsMax += getRegisterPacked(op.getReg()) - 1;\n    }\n  }\n  if (numOperandsMax == 0) {\n    // no operand to analyse\n    return;\n  }\n  instAnalysis->operands = new OperandAnalysis[numOperandsMax]();\n  // limit operandDescription\n  unsigned maxOperandDesc = desc.getNumOperands();\n  if (desc.isVariadic()) {\n    QBDI_REQUIRE(maxOperandDesc >= 1 or numOperands == 0);\n    maxOperandDesc--;\n  } else {\n    QBDI_REQUIRE(numOperands == maxOperandDesc);\n  }\n  // find written registers\n  std::bitset<32> regWrites;\n  for (unsigned i = 0, e = desc.getNumDefs(); i != e; ++i) {\n    const llvm::MCOperand &op = inst.getOperand(i);\n    if (op.isReg()) {\n      regWrites.set(i, true);\n    }\n  }\n  if (desc.isVariadic() and variadicOpsIsWrite(inst)) {\n    for (unsigned i = maxOperandDesc, e = inst.getNumOperands(); i != e; ++i) {\n      const llvm::MCOperand &op = inst.getOperand(i);\n      if (op.isReg()) {\n        regWrites.set(i, true);\n      }\n    }\n  }\n  // for each instruction operands\n  for (unsigned i = operandBias; i < numOperands; i++) {\n    const llvm::MCOperand &op = inst.getOperand(i);\n    // if the instruction is variadic, the opdesc for the variadic operand is\n    // the last opdesc\n    const llvm::MCOperandInfo &opdesc =\n        desc.operands()[std::min(i, maxOperandDesc)];\n    // fill a new operand analysis\n    QBDI_REQUIRE(instAnalysis->numOperands < numOperandsMax);\n    OperandAnalysis &opa = instAnalysis->operands[instAnalysis->numOperands];\n    // reinitialise the opa if a previous iteration has write some value\n    memset(&opa, 0, sizeof(OperandAnalysis));\n    opa.regCtxIdx = -1;\n    if (!op.isValid()) {\n      continue;\n    }\n    if (op.isReg()) {\n      unsigned int regNo = op.getReg();\n      int tied_to = desc.getOperandConstraint(i, llvm::MCOI::TIED_TO);\n\n      // if the operand is tied_to the previous operand, add the access to the\n      // operand.\n      if (tied_to != -1 and operandBias == 0) {\n        QBDI_REQUIRE(tied_to == i - 1);\n        QBDI_REQUIRE(inst.getOperand(i - 1).isReg());\n        if constexpr (is_arm)\n          QBDI_REQUIRE(not isFlagOperand(inst.getOpcode(), i, opdesc));\n        QBDI_REQUIRE(not isFlagRegister(regNo));\n        instAnalysis->operands[instAnalysis->numOperands - 1].regAccess |=\n            regWrites.test(i) ? REGISTER_WRITE : REGISTER_READ;\n        continue;\n      }\n      // don't reject regNo == 0 tp keep the same position of operand\n      // ex : \"lea rax, [rbx+10]\" and \"lea rax, [rbx+4*rcx+10]\" will have the\n      // same number of operand\n      if constexpr (is_arm) {\n        if (isFlagOperand(inst.getOpcode(), i, opdesc)) {\n          continue;\n        }\n      } else {\n        if (isFlagRegister(regNo)) {\n          instAnalysis->flagsAccess |=\n              regWrites.test(i) ? REGISTER_WRITE : REGISTER_READ;\n          continue;\n        }\n      }\n      // fill the operand analysis\n      analyseRegister(opa, regNo, MRI);\n      switch (opdesc.OperandType) {\n        case llvm::MCOI::OPERAND_REGISTER:\n          break;\n        case llvm::MCOI::OPERAND_MEMORY:\n          opa.flag |= OPERANDFLAG_ADDR;\n          break;\n        case llvm::MCOI::OPERAND_UNKNOWN:\n          opa.flag |= OPERANDFLAG_UNDEFINED_EFFECT;\n          break;\n        default:\n          QBDI_WARN(\"Not supported operandType {} for register operand\",\n                    opdesc.OperandType);\n          continue;\n      }\n      if (regNo != 0) {\n        opa.regAccess = regWrites.test(i) ? REGISTER_WRITE : REGISTER_READ;\n\n        // verify if the register is allocate in the same place as anothe\n        // register\n        if (tied_to != -1 and operandBias != 0) {\n          opa.regAccess |=\n              regWrites.test(tied_to) ? REGISTER_WRITE : REGISTER_READ;\n        }\n      }\n      instAnalysis->numOperands++;\n      if (opa.type == OPERAND_INVALID and getRegisterPacked(regNo) > 1) {\n        breakPackedRegister(instAnalysis, opa, regNo, MRI);\n        QBDI_REQUIRE(instAnalysis->numOperands <= numOperandsMax);\n      }\n    } else if (op.isImm()) {\n      if (isFlagOperand(inst.getOpcode(), i, opdesc)) {\n        continue;\n      }\n      // fill the operand analysis\n      switch (opdesc.OperandType) {\n        case llvm::MCOI::OPERAND_IMMEDIATE:\n          opa.size = getImmediateSize(inst, llvmcpu);\n          break;\n        case llvm::MCOI::OPERAND_MEMORY:\n          opa.flag |= OPERANDFLAG_ADDR;\n          opa.size = sizeof(rword);\n          break;\n        case llvm::MCOI::OPERAND_PCREL:\n          opa.size = getImmediateSize(inst, llvmcpu);\n          opa.flag |= OPERANDFLAG_PCREL;\n          break;\n        case llvm::MCOI::OPERAND_UNKNOWN:\n          opa.flag |= OPERANDFLAG_UNDEFINED_EFFECT;\n          opa.size = sizeof(rword);\n          break;\n        default:\n          QBDI_WARN(\"Not supported operandType {} for immediate operand\",\n                    opdesc.OperandType);\n          continue;\n      }\n      if (opdesc.isPredicate()) {\n        opa.type = OPERAND_PRED;\n      } else {\n        opa.type = OPERAND_IMM;\n      }\n      opa.value = getFixedOperandValue(inst, llvmcpu, i, op.getImm());\n      instAnalysis->numOperands++;\n    }\n  }\n\n  // analyse implicit registers (R/W)\n  analyseImplicitRegisters(instAnalysis, desc.implicit_uses(), REGISTER_READ,\n                           MRI);\n  analyseImplicitRegisters(instAnalysis, desc.implicit_defs(), REGISTER_WRITE,\n                           MRI);\n\n  // (R|E)SP are missing for RET and CALL in x86\n  getAdditionnalOperand(instAnalysis, inst, desc, MRI);\n\n  // reduce operands buffer if possible\n  if (instAnalysis->numOperands < numOperandsMax) {\n    OperandAnalysis *oldbuff = instAnalysis->operands;\n    if (instAnalysis->numOperands == 0) {\n      instAnalysis->operands = nullptr;\n    } else {\n      instAnalysis->operands = new OperandAnalysis[instAnalysis->numOperands]();\n      memcpy(instAnalysis->operands, oldbuff,\n             sizeof(OperandAnalysis) * instAnalysis->numOperands);\n    }\n    delete[] oldbuff;\n  }\n}\n\n} // namespace InstructionAnalysis\n\nvoid InstAnalysisDestructor::operator()(InstAnalysis *ptr) const {\n  if (ptr == nullptr) {\n    return;\n  }\n  if (ptr->operands != nullptr) {\n    delete[] ptr->operands;\n  }\n  if (ptr->disassembly != nullptr) {\n    delete[] ptr->disassembly;\n  }\n  delete ptr;\n}\n\nInstAnalysis *analyzeInstMetadata(const InstMetadata &instMetadata,\n                                  AnalysisType type, const LLVMCPU &llvmcpu) {\n\n  InstAnalysis *instAnalysis = instMetadata.analysis.get();\n  if (instAnalysis == nullptr) {\n    instAnalysis = new InstAnalysis;\n    // set all values to NULL/0/false\n    memset(instAnalysis, 0, sizeof(InstAnalysis));\n    instMetadata.analysis.reset(instAnalysis);\n  }\n\n  uint32_t oldType = instAnalysis->analysisType;\n  // ANALYSIS_JIT is not managed here, but in the ExecBlock\n  uint32_t supportedType = ANALYSIS_DISASSEMBLY | ANALYSIS_INSTRUCTION |\n                           ANALYSIS_OPERANDS | ANALYSIS_SYMBOL;\n  uint32_t newType = (oldType | type) & supportedType;\n  uint32_t missingType = (oldType ^ newType) & supportedType;\n\n  if (missingType == 0) {\n    return instAnalysis;\n  }\n\n  instAnalysis->analysisType |= newType;\n\n  const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n  const llvm::MCInst &inst = instMetadata.inst;\n  const llvm::MCInstrDesc &desc = MCII.get(inst.getOpcode());\n\n  if (missingType & ANALYSIS_DISASSEMBLY) {\n    std::string buffer = llvmcpu.showInst(inst, instMetadata.address);\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n    // support for prefix that isn't include in instruction (like LOCK_PREFIX)\n    if (instMetadata.prefix.size() > 0) {\n      std::string prefix;\n      for (const auto &prefixInst : instMetadata.prefix) {\n        prefix += llvmcpu.showInst(prefixInst, instMetadata.address) + \"\\t\";\n      }\n      buffer = prefix + buffer;\n    }\n#endif\n\n    int len = buffer.size() + 1;\n    instAnalysis->disassembly = new char[len];\n    strncpy(instAnalysis->disassembly, buffer.c_str(), len);\n    buffer.clear();\n  }\n\n  if (missingType & ANALYSIS_INSTRUCTION) {\n    instAnalysis->address = instMetadata.address;\n    instAnalysis->instSize = instMetadata.instSize;\n    instAnalysis->cpuMode = instMetadata.cpuMode;\n    instAnalysis->affectControlFlow = instMetadata.modifyPC;\n    instAnalysis->isBranch = desc.isBranch();\n    instAnalysis->isCall = desc.isCall();\n    instAnalysis->isReturn = desc.isReturn();\n    instAnalysis->isCompare = desc.isCompare();\n    instAnalysis->isPredicable = desc.isPredicable();\n    instAnalysis->isMoveImm = desc.isMoveImmediate();\n    instAnalysis->loadSize = getReadSize(inst, llvmcpu);\n    instAnalysis->storeSize = getWriteSize(inst, llvmcpu);\n    instAnalysis->mayLoad =\n        instAnalysis->loadSize != 0 || unsupportedRead(inst);\n    instAnalysis->mayStore =\n        instAnalysis->storeSize != 0 || unsupportedWrite(inst);\n    instAnalysis->opcode_LLVM = inst.getOpcode();\n    instAnalysis->mayLoad_LLVM = desc.mayLoad();\n    instAnalysis->mayStore_LLVM = desc.mayStore();\n    instAnalysis->mnemonic = llvmcpu.getInstOpcodeName(inst);\n\n    InstructionAnalysis::analyseCondition(instAnalysis, inst, desc, llvmcpu);\n  }\n\n  if (missingType & ANALYSIS_OPERANDS) {\n    if constexpr (is_arm) {\n      // for arm, analyseCondition analyse the usage of the flags\n      if (not(missingType & ANALYSIS_INSTRUCTION)) {\n        InstructionAnalysis::analyseCondition(instAnalysis, inst, desc,\n                                              llvmcpu);\n      }\n    }\n    // analyse operands (immediates / registers)\n    InstructionAnalysis::analyseOperands(instAnalysis, inst, llvmcpu);\n  }\n\n  if (missingType & ANALYSIS_SYMBOL) {\n    // find nearest symbol (if any)\n#ifndef QBDI_PLATFORM_WINDOWS\n    Dl_info info;\n    const char *ptr;\n\n    int ret = dladdr((void *)instAnalysis->address, &info);\n    if (ret != 0) {\n      if (info.dli_sname) {\n        instAnalysis->symbolName = info.dli_sname;\n        instAnalysis->symbolOffset =\n            instAnalysis->address - (rword)info.dli_saddr;\n      }\n      if (info.dli_fname) {\n        // dirty basename, but thead safe\n        if ((ptr = strrchr(info.dli_fname, '/')) != nullptr) {\n          instAnalysis->moduleName = ptr + 1;\n        }\n      }\n    }\n#endif\n  }\n\n  return instAnalysis;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/InstAnalysis_prive.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTANALYSISPRIVE_H\n#define INSTANALYSISPRIVE_H\n\n#include <memory>\n\n#include \"QBDI/InstAnalysis.h\"\n\n#include \"Patch/Types.h\"\n\nnamespace llvm {\nclass MCInstrDesc;\nclass MCInstrInfo;\nclass MCOperandInfo;\nclass MCRegisterInfo;\n} // namespace llvm\n\nnamespace QBDI {\n\nclass InstMetadata;\nclass LLVMCPU;\n\nstruct InstAnalysisDestructor {\n  void operator()(InstAnalysis *ptr) const;\n};\n\nusing InstAnalysisPtr = std::unique_ptr<InstAnalysis, InstAnalysisDestructor>;\n\nInstAnalysis *analyzeInstMetadata(const InstMetadata &instMetadata,\n                                  AnalysisType type, const LLVMCPU &llvmcpu);\nnamespace InstructionAnalysis {\n\nConditionType ConditionLLVM2QBDI(unsigned cond);\n\nvoid analyseRegister(OperandAnalysis &opa, RegLLVM regNo,\n                     const llvm::MCRegisterInfo &MRI);\nvoid tryMergeCurrentRegister(InstAnalysis *instAnalysis);\n\n// Arch specific\n// =============\n\nvoid analyseCondition(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                      const llvm::MCInstrDesc &desc, const LLVMCPU &llvmcpu);\nbool isFlagOperand(unsigned opcode, unsigned opNum,\n                   const llvm::MCOperandInfo &opdesc);\nunsigned getBias(const llvm::MCInstrDesc &desc);\n\n// Missing operand in LLVM Description\nunsigned getAdditionnalOperandNumber(const llvm::MCInst &inst,\n                                     const llvm::MCInstrDesc &desc);\nvoid getAdditionnalOperand(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                           const llvm::MCInstrDesc &desc,\n                           const llvm::MCRegisterInfo &MRI);\n\n} // namespace InstructionAnalysis\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Utility/LogSys.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <utility>\n\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/Patch.h\"\n#include \"Utility/LogSys.h\"\n\n#include \"spdlog/common.h\"\n#include \"spdlog/fmt/bin_to_hex.h\"\n#include \"spdlog/sinks/basic_file_sink.h\"\n#include \"spdlog/sinks/stdout_color_sinks.h\"\n\n#ifdef QBDI_PLATFORM_ANDROID\n#include \"spdlog/sinks/android_sink.h\"\n#endif\n\n// windows seem to define a macro on ERROR\n#ifdef QBDI_PLATFORM_WINDOWS\n#ifdef ERROR\n#undef ERROR\n#endif\n#endif\n\nnamespace QBDI {\nnamespace {\n\nclass Logger {\npublic:\n  Logger(void) {\n    setDefaultLogger();\n    spdlog::set_pattern(\"%^[%l] (%!) %s:%#%$ %v\");\n    spdlog::set_level(spdlog::level::info);\n  }\n\n  Logger(const Logger &) = delete;\n\n  Logger &operator=(const Logger &) = delete;\n\n  void setPriority(LogPriority priority) {\n    switch (priority) {\n      case LogPriority::DEBUG:\n        spdlog::set_level(spdlog::level::debug);\n        break;\n      default:\n      case LogPriority::INFO:\n        spdlog::set_level(spdlog::level::info);\n        break;\n      case LogPriority::WARNING:\n        spdlog::set_level(spdlog::level::warn);\n        break;\n      case LogPriority::ERROR:\n        spdlog::set_level(spdlog::level::err);\n        break;\n      case LogPriority::DISABLE:\n        spdlog::set_level(spdlog::level::off);\n        break;\n    }\n  }\n\n  void setFile(const std::string &f, bool truncate) {\n    sink = spdlog::basic_logger_mt(\"QBDI\", f, truncate);\n    spdlog::set_default_logger(sink);\n  }\n\n  void setDefaultLogger() {\n#ifdef QBDI_PLATFORM_ANDROID\n    sink = spdlog::android_logger_mt(\"QBDI\", \"qbdi\");\n#else\n    sink = spdlog::stderr_color_mt(\"console\");\n#endif\n    spdlog::set_default_logger(sink);\n  }\n\n  void setConsoleLogger() {\n    sink = spdlog::stderr_color_mt(\"console\");\n    spdlog::set_default_logger(sink);\n  }\n\n  ~Logger(void) = default;\n\nprivate:\n  std::shared_ptr<spdlog::logger> sink;\n};\n\nstatic Logger logger;\n\n} // namespace\n\n// =========================\n\nvoid qbdi_setLogFile(const char *filename, bool truncate) {\n  logger.setFile(std::string(filename), truncate);\n}\n\nvoid setLogFile(const std::string &filename, bool truncate) {\n  logger.setFile(filename, truncate);\n}\n\nvoid qbdi_setLogPriority(LogPriority priority) { logger.setPriority(priority); }\n\nvoid qbdi_setLogConsole() { logger.setConsoleLogger(); }\n\nvoid qbdi_setLogDefault() { logger.setDefaultLogger(); }\n\n// ====================================================\n\nstd::string format_as(const QBDI::Patch &patch) {\n\n  std::string disass =\n      patch.llvmcpu->showInst(patch.metadata.inst, patch.metadata.address);\n  unsigned opcode = patch.metadata.inst.getOpcode();\n  const char *opcodeName = patch.llvmcpu->getInstOpcodeName(opcode);\n\n#ifdef SPDLOG_USE_STD_FORMAT\n  std::string message = std::vformat(\n#else\n  std::string message = fmt::format(\n#endif\n      \"0x{:x}: {} | {} ({}) | ({:n}){}\", patch.metadata.address, disass,\n      opcodeName, opcode,\n      spdlog::to_hex(reinterpret_cast<uint8_t *>(patch.metadata.address),\n                     reinterpret_cast<uint8_t *>(patch.metadata.endAddress())),\n#if defined(QBDI_ARCH_ARM)\n      (patch.llvmcpu->getCPUMode() == QBDI::CPUMode::ARM) ? \" (ARM mode)\"\n                                                          : \" (Thumb mode)\"\n#else\n      \"\"\n#endif\n  );\n  return message;\n}\n\nrword format_as(Options opt) { return fmt::underlying(opt); }\nunsigned format_as(CPUMode mode) { return fmt::underlying(mode); }\nunsigned format_as(InstPosition pos) { return fmt::underlying(pos); }\nunsigned format_as(AnalysisType t) { return fmt::underlying(t); }\nunsigned format_as(const RegLLVM &r) { return r.getValue(); }\nunsigned format_as(const Reg &r) { return r.getID(); }\nrword format_as(const Shadow &s) { return s.getTag(); }\nuint16_t format_as(ShadowReservedTag tag) { return tag; }\nrword format_as(const Constant &cst) { return cst; }\nint64_t format_as(const Offset &off) { return off; }\nunsigned format_as(const Temp &t) { return t; }\nunsigned format_as(const Operand &op) { return op; }\nunsigned format_as(RelocatableInstTag tag) { return tag; }\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/LogSys.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LOGSYS_H\n#define LOGSYS_H\n\n#include <memory>\n#include <string>\n\n#include \"Patch/Types.h\"\n\n#include \"QBDI/Callback.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/Logs.h\"\n#include \"QBDI/Options.h\"\n#include \"QBDI/State.h\"\n#include \"spdlog/spdlog.h\"\n\nnamespace llvm {\nclass MCInst;\n}\n\nnamespace QBDI {\nclass LLVMCPU;\nclass InstMetadata;\nclass Patch;\n\n#define QBDI_INFO(...) SPDLOG_INFO(__VA_ARGS__)\n#define QBDI_WARN(...) SPDLOG_WARN(__VA_ARGS__)\n#define QBDI_ERROR(...) SPDLOG_ERROR(__VA_ARGS__)\n#define QBDI_CRITICAL(...) SPDLOG_CRITICAL(__VA_ARGS__)\n\n#if defined(QBDI_LOG_DEBUG)\n#define QBDI_DEBUG_BLOCK(block) block\n#define QBDI_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__)\n#else\n#define QBDI_DEBUG_BLOCK(block) \\\n  {                             \\\n    (void)0;                    \\\n  }\n#define QBDI_DEBUG(...) (void)0\n#endif\n\n#define QBDI_REQUIRE(req)                      \\\n  if (!(req)) {                                \\\n    QBDI_ERROR(\"Assertion Failed : {}\", #req); \\\n  }\n#define QBDI_REQUIRE_ACTION(req, ac)           \\\n  if (!(req)) {                                \\\n    QBDI_ERROR(\"Assertion Failed : {}\", #req); \\\n    ac;                                        \\\n  }\n#define QBDI_REQUIRE_ABORT(req, ...) \\\n  if (!(req)) {                      \\\n    QBDI_CRITICAL(__VA_ARGS__);      \\\n    spdlog::shutdown();              \\\n    abort();                         \\\n  }\n#define QBDI_ABORT(...)         \\\n  do {                          \\\n    QBDI_CRITICAL(__VA_ARGS__); \\\n    spdlog::shutdown();         \\\n    abort();                    \\\n  } while (0);\n\n// formatter of internal enum\n\nstd::string format_as(const QBDI::Patch &patch);\nrword format_as(Options opt);\nunsigned format_as(CPUMode mode);\nunsigned format_as(InstPosition pos);\nunsigned format_as(AnalysisType t);\nunsigned format_as(const RegLLVM &r);\nunsigned format_as(const Reg &r);\nrword format_as(const Shadow &s);\nuint16_t format_as(ShadowReservedTag tag);\nrword format_as(const Constant &cst);\nint64_t format_as(const Offset &off);\nunsigned format_as(const Temp &t);\nunsigned format_as(const Operand &op);\nunsigned format_as(RelocatableInstTag tag);\n\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "src/Utility/Memory.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <iterator>\n#include <memory>\n#include <set>\n#include <stdarg.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string>\n#include <vector>\n\n#include \"QBDI/Config.h\"\n#include \"QBDI/Memory.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n#include \"Utility/LogSys.h\"\n\n#define FRAME_LENGTH 16\n\nnamespace QBDI {\n\n// C++ method\nstd::vector<std::string> getModuleNames() {\n  std::set<std::string> modules;\n\n  for (const MemoryMap &m : getCurrentProcessMaps(false))\n    if (!m.name.empty())\n      modules.insert(m.name);\n\n  return {std::begin(modules), std::end(modules)};\n}\n\nvoid *alignedAlloc(size_t size, size_t align) {\n  void *allocated = nullptr;\n  // Alignment needs to be a power of 2\n  if ((align == 0) || ((align & (align - 1)) != 0)) {\n    return nullptr;\n  }\n#if defined(QBDI_PLATFORM_WINDOWS)\n  allocated = _aligned_malloc(size, align);\n#else\n  int ret = posix_memalign(&allocated, align, size);\n  if (ret != 0) {\n    return nullptr;\n  }\n#endif\n  return allocated;\n}\n\nvoid alignedFree(void *ptr) {\n#if defined(QBDI_PLATFORM_WINDOWS)\n  _aligned_free(ptr);\n#else\n  free(ptr);\n#endif\n}\n\nbool allocateVirtualStack(GPRState *ctx, uint32_t stackSize, uint8_t **stack) {\n  (*stack) = static_cast<uint8_t *>(alignedAlloc(stackSize, 16));\n  if (*stack == nullptr) {\n    return false;\n  }\n\n  QBDI_GPR_SET(ctx, REG_SP, reinterpret_cast<QBDI::rword>(*stack) + stackSize);\n  QBDI_GPR_SET(ctx, REG_BP, QBDI_GPR_GET(ctx, REG_SP));\n\n  return true;\n}\n\nvoid simulateCall(GPRState *ctx, rword returnAddress,\n                  const std::vector<rword> &args) {\n  simulateCallA(ctx, returnAddress, args.size(), args.data());\n}\n\nvoid simulateCallV(GPRState *ctx, rword returnAddress, uint32_t argNum,\n                   va_list ap) {\n  std::vector<rword> args(argNum);\n  for (uint32_t i = 0; i < argNum; i++) {\n    args[i] = va_arg(ap, rword);\n  }\n  simulateCallA(ctx, returnAddress, argNum, args.data());\n}\n\nvoid simulateCallA(GPRState *ctx, rword returnAddress, uint32_t argNum,\n                   const rword *args) {\n  uint32_t i = 0;\n  uint32_t argsoff = 0;\n  uint32_t limit = FRAME_LENGTH;\n\n  // Allocate arguments frame\n  QBDI_GPR_SET(ctx, REG_SP,\n               QBDI_GPR_GET(ctx, REG_SP) - FRAME_LENGTH * sizeof(rword));\n\n  // Handle the return address\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  QBDI_GPR_SET(ctx, REG_SP, QBDI_GPR_GET(ctx, REG_SP) - sizeof(rword));\n  *(rword *)(QBDI_GPR_GET(ctx, REG_SP)) = returnAddress;\n  argsoff++;\n#elif defined(QBDI_ARCH_ARM) || defined(QBDI_ARCH_AARCH64)\n  QBDI_DEBUG(\"Set LR to: 0x{:x}\", returnAddress);\n  ctx->lr = returnAddress;\n#endif\n\n#define UNSTACK_ARG(REG)  \\\n  if (i < argNum) {       \\\n    ctx->REG = args[i++]; \\\n  }\n#if defined(QBDI_ARCH_X86_64)\n#if defined(QBDI_PLATFORM_WINDOWS)\n  // Shadow space\n  argsoff += 4;\n  // Register args\n  UNSTACK_ARG(rcx);\n  UNSTACK_ARG(rdx);\n  UNSTACK_ARG(r8);\n  UNSTACK_ARG(r9);\n#else\n  // Register args\n  UNSTACK_ARG(rdi);\n  UNSTACK_ARG(rsi);\n  UNSTACK_ARG(rdx);\n  UNSTACK_ARG(rcx);\n  UNSTACK_ARG(r8);\n  UNSTACK_ARG(r9);\n#endif // OS\n#elif defined(QBDI_ARCH_X86)\n  // no register used\n#elif defined(QBDI_ARCH_ARM)\n  UNSTACK_ARG(r0);\n  UNSTACK_ARG(r1);\n  UNSTACK_ARG(r2);\n  UNSTACK_ARG(r3);\n#elif defined(QBDI_ARCH_AARCH64)\n  UNSTACK_ARG(x0);\n  UNSTACK_ARG(x1);\n  UNSTACK_ARG(x2);\n  UNSTACK_ARG(x3);\n  UNSTACK_ARG(x4);\n  UNSTACK_ARG(x5);\n  UNSTACK_ARG(x6);\n  UNSTACK_ARG(x7);\n#endif // ARCH\n#undef UNSTACK_ARG\n  limit -= argsoff;\n\n  // Push remaining args on the stack\n  rword *frame = (rword *)QBDI_GPR_GET(ctx, REG_SP);\n  for (uint32_t j = 0; (i + j) < argNum && j < limit; j++) {\n    frame[argsoff + j] = args[i + j];\n  }\n}\n\n// C method\nqbdi_MemoryMap *convert_MemoryMap_to_C(std::vector<MemoryMap> maps,\n                                       size_t *size) {\n  if (size == NULL)\n    return NULL;\n  *size = maps.size();\n  if (*size == 0) {\n    return NULL;\n  }\n  qbdi_MemoryMap *cmaps =\n      (qbdi_MemoryMap *)malloc(*size * sizeof(qbdi_MemoryMap));\n  QBDI_REQUIRE_ABORT(cmaps != NULL, \"Allocation Fail\");\n  for (size_t i = 0; i < *size; i++) {\n    cmaps[i].start = maps[i].range.start();\n    cmaps[i].end = maps[i].range.end();\n    cmaps[i].permission = static_cast<qbdi_Permission>(maps[i].permission);\n    cmaps[i].name = strdup(maps[i].name.c_str());\n  }\n  return cmaps;\n}\n\nqbdi_MemoryMap *qbdi_getRemoteProcessMaps(rword pid, bool full_path,\n                                          size_t *size) {\n  if (size == NULL)\n    return NULL;\n  return convert_MemoryMap_to_C(getRemoteProcessMaps(pid, full_path), size);\n}\n\nqbdi_MemoryMap *qbdi_getCurrentProcessMaps(bool full_path, size_t *size) {\n  if (size == NULL)\n    return NULL;\n  return convert_MemoryMap_to_C(getCurrentProcessMaps(full_path), size);\n}\n\nvoid qbdi_freeMemoryMapArray(qbdi_MemoryMap *arr, size_t size) {\n  for (size_t i = 0; i < size; i++) {\n    if (arr[i].name) {\n      free(arr[i].name);\n    }\n  }\n  free(arr);\n}\n\nchar **qbdi_getModuleNames(size_t *size) {\n  if (size == NULL)\n    return NULL;\n  std::vector<std::string> modules = getModuleNames();\n  *size = modules.size();\n  if (*size == 0) {\n    return NULL;\n  }\n  char **names = (char **)malloc(modules.size() * sizeof(char *));\n  QBDI_REQUIRE_ABORT(names != NULL, \"Allocation Fail\");\n  for (size_t i = 0; i < modules.size(); i++) {\n    names[i] = strdup(modules[i].c_str());\n  }\n  return names;\n}\n\nvoid *qbdi_alignedAlloc(size_t size, size_t align) {\n  return alignedAlloc(size, align);\n}\n\nvoid qbdi_alignedFree(void *ptr) { alignedFree(ptr); }\n\nbool qbdi_allocateVirtualStack(GPRState *ctx, uint32_t stackSize,\n                               uint8_t **stack) {\n  return allocateVirtualStack(ctx, stackSize, stack);\n}\n\nvoid qbdi_simulateCall(GPRState *ctx, rword returnAddress, uint32_t argNum,\n                       ...) {\n  va_list ap;\n  // Handle the arguments\n  va_start(ap, argNum);\n  simulateCallV(ctx, returnAddress, argNum, ap);\n  va_end(ap);\n}\nvoid qbdi_simulateCallV(GPRState *ctx, rword returnAddress, uint32_t argNum,\n                        va_list ap) {\n  simulateCallV(ctx, returnAddress, argNum, ap);\n}\n\nvoid qbdi_simulateCallA(GPRState *ctx, rword returnAddress, uint32_t argNum,\n                        const rword *args) {\n  simulateCallA(ctx, returnAddress, argNum, args);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/Memory_linux.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <string>\n#include <unistd.h>\n#include <vector>\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Range.h\"\n#include \"QBDI/State.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace QBDI {\n\nstd::vector<MemoryMap> getCurrentProcessMaps(bool full_path) {\n  return getRemoteProcessMaps(getpid(), full_path);\n}\n\nstd::vector<MemoryMap> getRemoteProcessMaps(QBDI::rword pid, bool full_path) {\n  static const int BUFFER_SIZE = 256;\n  char line[BUFFER_SIZE] = {0};\n  FILE *mapfile = nullptr;\n  std::vector<MemoryMap> maps;\n\n  snprintf(line, BUFFER_SIZE, \"/proc/%llu/maps\", (unsigned long long)pid);\n  mapfile = fopen(line, \"r\");\n  QBDI_DEBUG(\"Querying memory maps from {}\", line);\n  QBDI_REQUIRE_ACTION(mapfile != nullptr, return maps);\n\n  // Process a memory map line in the form of\n  // 00400000-0063c000 r-xp 00000000 fe:01 675628    /usr/bin/vim\n  while (fgets(line, BUFFER_SIZE, mapfile) != nullptr) {\n    char *ptr = nullptr;\n    MemoryMap m;\n\n    // Remove \\n\n    if ((ptr = strchr(line, '\\n')) != nullptr) {\n      *ptr = '\\0';\n    }\n    ptr = line;\n    QBDI_DEBUG(\"Parsing line: {}\", line);\n\n    // Read range\n    m.range.setStart(strtoul(ptr, &ptr, 16));\n    ptr++; // '-'\n    m.range.setEnd(strtoul(ptr, &ptr, 16));\n\n    // skip the spaces\n    while (isspace(*ptr))\n      ptr++;\n\n    // Read the permission\n    m.permission = QBDI::PF_NONE;\n    if (*ptr == 'r')\n      m.permission |= QBDI::PF_READ;\n    ptr++;\n    if (*ptr == 'w')\n      m.permission |= QBDI::PF_WRITE;\n    ptr++;\n    if (*ptr == 'x')\n      m.permission |= QBDI::PF_EXEC;\n    ptr++;\n    ptr++; // skip the protected\n\n    // skip the spaces\n    while (isspace(*ptr))\n      ptr++;\n\n    // Discard the file offset\n    strtoul(ptr, &ptr, 16);\n\n    // skip the spaces\n    while (isspace(*ptr))\n      ptr++;\n\n    // Discard the device id\n    strtoul(ptr, &ptr, 16);\n    ptr++; // ':'\n    strtoul(ptr, &ptr, 16);\n\n    // skip the spaces\n    while (isspace(*ptr))\n      ptr++;\n\n    // Discard the inode\n    strtoul(ptr, &ptr, 10);\n\n    // skip the spaces\n    while (isspace(*ptr))\n      ptr++;\n\n    // Get the file name\n    if (full_path) {\n      m.name = ptr;\n    } else {\n      if ((ptr = strrchr(ptr, '/')) != nullptr) {\n        m.name = ptr + 1;\n      } else {\n        m.name.clear();\n      }\n    }\n\n    QBDI_DEBUG(\"Read new map [0x{:x}, 0x{:x}] {} {}{}{}\", m.range.start(),\n               m.range.end(), m.name.c_str(),\n               (m.permission & QBDI::PF_READ) ? \"r\" : \"-\",\n               (m.permission & QBDI::PF_WRITE) ? \"w\" : \"-\",\n               (m.permission & QBDI::PF_EXEC) ? \"x\" : \"-\");\n    maps.push_back(m);\n  }\n  fclose(mapfile);\n  return maps;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/Memory_macos.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"llvm/Support/Process.h\"\n\n#include \"QBDI/Memory.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"Utility/LogSys.h\"\n\n#include <set>\n\n#include <mach-o/dyld.h>\n#include <mach-o/dyld_images.h>\n#include <mach-o/getsect.h>\n#include <mach-o/loader.h>\n#include <mach/mach.h>\n\nnamespace QBDI {\n\n#ifdef QBDI_BITS_64\n#define STRUCT_HEADER mach_header_64\n#define STRUCT_SEG segment_command_64\n#define MAGIC_HEADER MH_MAGIC_64\n#define MAGIC_SEG LC_SEGMENT_64\n#else\n#define STRUCT_HEADER mach_header\n#define STRUCT_SEG segment_command\n#define MAGIC_HEADER MH_MAGIC\n#define MAGIC_SEG LC_SEGMENT\n#endif\n\nmach_vm_address_t getDyldAllImageInfoAddr(task_t task,\n                                          mach_vm_size_t *all_image_info_size) {\n  task_dyld_info_data_t dyld_info;\n  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;\n  kern_return_t kr = task_info(\n      task, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);\n  if (kr != KERN_SUCCESS) {\n    return 0;\n  }\n  if (all_image_info_size) {\n    *all_image_info_size = dyld_info.all_image_info_size;\n  }\n  return dyld_info.all_image_info_addr;\n}\n\nbool getDyldAllImageInfo(task_t task,\n                         struct dyld_all_image_infos *all_image_infos) {\n  if (!all_image_infos) {\n    return false;\n  }\n  mach_vm_size_t all_image_info_size;\n  mach_vm_address_t all_image_info_addr =\n      getDyldAllImageInfoAddr(task, &all_image_info_size);\n  vm_size_t size = sizeof(struct dyld_all_image_infos);\n  memset(all_image_infos, 0, size);\n  kern_return_t kr = vm_read_overwrite(task, all_image_info_addr, size,\n                                       (vm_address_t)all_image_infos, &size);\n  if (kr != KERN_SUCCESS) {\n    return false;\n  }\n  return true;\n}\n\nstruct dyld_image_info *\ngetImageInfo(task_t task, struct dyld_all_image_infos &all_image_infos,\n             uint32_t i) {\n  vm_size_t size = sizeof(struct dyld_image_info);\n  struct dyld_image_info *image_info =\n      (struct dyld_image_info *)calloc(1, size);\n  // If are asking for more than image count, let's return dyld\n  if (all_image_infos.infoArrayCount <= i) {\n    image_info->imageLoadAddress = all_image_infos.dyldImageLoadAddress;\n    if (all_image_infos.version >= 15) {\n      image_info->imageFilePath = all_image_infos.dyldPath;\n    }\n    image_info->imageFileModDate = -1;\n  } else {\n    kern_return_t kr =\n        vm_read_overwrite(task, (vm_address_t) & (all_image_infos.infoArray[i]),\n                          size, (vm_address_t)image_info, &size);\n    if (kr != KERN_SUCCESS) {\n      return NULL;\n    }\n  }\n  return image_info;\n}\n\nstruct STRUCT_HEADER *getImageHeader(task_t task,\n                                     const struct dyld_image_info *image_info) {\n  struct STRUCT_HEADER mh;\n  vm_size_t size = sizeof(struct STRUCT_HEADER);\n  // read macho header first bytes\n  kern_return_t kr =\n      vm_read_overwrite(task, (vm_address_t)image_info->imageLoadAddress, size,\n                        (vm_address_t)&mh, &size);\n  if ((kr != KERN_SUCCESS) || (mh.magic != MAGIC_HEADER)) {\n    return NULL;\n  }\n  // read whole macho header\n  size = mh.sizeofcmds + sizeof(struct STRUCT_HEADER);\n  struct STRUCT_HEADER *header = (struct STRUCT_HEADER *)malloc(size);\n  kr = vm_read_overwrite(task, (vm_address_t)image_info->imageLoadAddress, size,\n                         (vm_address_t)header, &size);\n  if (kr != KERN_SUCCESS) {\n    free(header);\n    return NULL;\n  }\n  return header;\n}\n\nchar *getImagePath(task_t task, const struct dyld_image_info *image_info) {\n  if (image_info->imageFilePath == nullptr) {\n    // see getImageInfo\n    if (image_info->imageFileModDate == -1) {\n      return strdup(\"/usr/lib/dyld\");\n    }\n    return NULL;\n  }\n  vm_size_t size = PATH_MAX;\n  char *imagePath = (char *)malloc(size);\n  kern_return_t kr =\n      vm_read_overwrite(task, (vm_address_t)image_info->imageFilePath, size,\n                        (vm_address_t)imagePath, &size);\n  if (kr != KERN_SUCCESS) {\n    free(imagePath);\n    return NULL;\n  }\n  char *ret = strdup(imagePath);\n  free(imagePath);\n  return ret;\n}\n\nuintptr_t getImageSlideWithHeader(const struct dyld_image_info *image_info,\n                                  const struct STRUCT_HEADER *mh) {\n  struct load_command *lc = NULL;\n  struct STRUCT_SEG *seg = NULL;\n\n  for (lc = (struct load_command *)((uintptr_t)mh +\n                                    sizeof(struct STRUCT_HEADER));\n       (uintptr_t)lc < (uintptr_t)mh + (uintptr_t)mh->sizeofcmds;\n       lc = (struct load_command *)((uintptr_t)lc + (uintptr_t)lc->cmdsize)) {\n    if (lc->cmd == MAGIC_SEG) {\n      seg = (struct STRUCT_SEG *)lc;\n      if (strcmp(seg->segname, \"__TEXT\") == 0)\n        return (char *)(image_info->imageLoadAddress) - (char *)(seg->vmaddr);\n    }\n  }\n  return 0;\n}\n\nstatic std::vector<MemoryMap> getProcessMaps(task_t task, bool full_path) {\n  uint32_t icnt = 0;\n  struct STRUCT_HEADER *mh = NULL;\n  char *path = NULL;\n  char *name = NULL;\n  uintptr_t slide = 0;\n  struct load_command *lc = NULL;\n  struct STRUCT_SEG *seg = NULL;\n\n  std::vector<MemoryMap> memMaps;\n  std::vector<MemoryMap> modMaps;\n  std::vector<MemoryMap> omaps;\n\n  vm_region_submap_short_info_data_64_t basic_info;\n  vm_address_t addr = 0, next = addr;\n  vm_size_t size = 0;\n  mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;\n  uint32_t depth = 1;\n  kern_return_t kr;\n\n  // Create a memory map\n  while (1) {\n    kr = vm_region_recurse_64(task, &next, &size, &depth,\n                              (vm_region_recurse_info_t)&basic_info, &count);\n    if (kr != KERN_SUCCESS) {\n      break;\n    }\n\n    if (basic_info.is_submap) {\n      depth++;\n      continue;\n    }\n\n    addr = next;\n    next += size;\n\n    memMaps.emplace_back(addr, next,\n                         static_cast<Permission>(basic_info.protection), \"\");\n  }\n\n  // Create a map of loaded images segments\n  struct dyld_all_image_infos all_image_infos;\n  bool res = getDyldAllImageInfo(task, &all_image_infos);\n  // add +1 in order to include dyld (see getImageInfo)\n  icnt = res ? all_image_infos.infoArrayCount + 1 : 0;\n\n  for (uint32_t i = 0; i < icnt; i++) {\n    struct dyld_image_info *image_info = getImageInfo(task, all_image_infos, i);\n    if (image_info) {\n      mh = getImageHeader(task, image_info);\n      if (mh) {\n        path = getImagePath(task, image_info);\n        if (path) {\n          slide = getImageSlideWithHeader(image_info, mh);\n\n          // dirty basename, but thead safe\n          name = strrchr(path, '/');\n          if (name++) {\n            // Scan all segments\n            for (lc = (struct load_command *)((uintptr_t)mh +\n                                              sizeof(struct STRUCT_HEADER));\n                 (uintptr_t)lc < (uintptr_t)mh + (uintptr_t)mh->sizeofcmds;\n                 lc = (struct load_command *)((uintptr_t)lc +\n                                              (uintptr_t)lc->cmdsize)) {\n              if (lc->cmd == MAGIC_SEG) {\n                seg = (struct STRUCT_SEG *)lc;\n                // Skip __ZERO segment\n                if (!seg->fileoff && !seg->filesize) {\n                  continue;\n                }\n\n                // Create a map entry\n                modMaps.emplace_back(seg->vmaddr + slide,\n                                     seg->vmaddr + slide + seg->vmsize,\n                                     QBDI::PF_NONE, (full_path) ? path : name);\n              }\n            }\n          }\n          free(path);\n        }\n        free(mh);\n      }\n      free(image_info);\n    }\n  }\n\n  // Merge our two maps into a consitent view of the memory\n  std::set<rword> inserted;\n  for (const MemoryMap &mem : memMaps) {\n    // create a range set of current memory range\n    RangeSet<rword> rs;\n    rs.add(mem.range);\n\n    // try to map modules to current memory range\n    for (const MemoryMap &mod : modMaps) {\n      if (mem.range.overlaps(mod.range)) {\n        // skip if already inserted (due to previous overlap)\n        if (inserted.count(mod.range.start()))\n          continue;\n        // add new entry in map\n        // do not add name to the shared __LINKEDIT\n        std::string name;\n        if (mem.permission != PF_READ || mod.range == mem.range) {\n          name = mod.name;\n        }\n        omaps.emplace_back(mod.range, mem.permission, name);\n        // mark range as inserted\n        inserted.insert(mod.range.start());\n        // remove module range from memory range\n        rs.remove(mod.range);\n      }\n    }\n    // add unmatched ranges in memory map\n    for (const Range<rword> &r : rs.getRanges()) {\n      omaps.emplace_back(r, mem.permission, \"\");\n    }\n  }\n  // sort the probably unordered map\n  std::sort(omaps.begin(), omaps.end(),\n            [](const MemoryMap &a, const MemoryMap &b) -> bool {\n              return a.range.start() < b.range.start();\n            });\n\n  return omaps;\n}\n\nstd::vector<MemoryMap> getCurrentProcessMaps(bool full_path) {\n  task_t task = 0;\n  kern_return_t kr = task_for_pid(mach_task_self(), getpid(), &task);\n  if (kr == KERN_SUCCESS) {\n    return getProcessMaps(task, full_path);\n  } else {\n    return getProcessMaps(mach_task_self(), full_path);\n  }\n}\n\nstd::vector<MemoryMap> getRemoteProcessMaps(QBDI::rword pid, bool full_path) {\n  task_t task = 0;\n  kern_return_t kr = task_for_pid(mach_task_self(), pid, &task);\n  QBDI_REQUIRE_ACTION(kr == KERN_SUCCESS, return {});\n  return getProcessMaps(task, full_path);\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/Memory_windows.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"llvm/Support/Process.h\"\n\n#include \"QBDI/Memory.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"Utility/LogSys.h\"\n\n// clang-format off\n#include <Windows.h>\n#include <Psapi.h>\n// clang-format on\n#ifdef UNICODE\n#include <codecvt>\n#include <locale>\n#endif\n\nnamespace QBDI {\n\n#define PROT_ISREAD(PROT) ((PROT) & 0xEE)\n#define PROT_ISWRITE(PROT) ((PROT) & 0xCC)\n#define PROT_ISEXEC(PROT) ((PROT) & 0xF0)\n\nstd::vector<MemoryMap> getCurrentProcessMaps(bool full_path) {\n  return getRemoteProcessMaps(GetCurrentProcessId(), full_path);\n}\n\nstd::vector<MemoryMap> getRemoteProcessMaps(QBDI::rword pid, bool full_path) {\n  std::vector<MemoryMap> maps;\n  MEMORY_BASIC_INFORMATION info;\n  HMODULE mod;\n  rword addr = 0;\n  rword next = addr;\n  SIZE_T size = 0;\n  TCHAR path[MAX_PATH];\n\n  HANDLE self = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |\n                                PROCESS_VM_READ,\n                            FALSE, pid);\n  if (self == NULL) {\n    return maps;\n  }\n\n  while (VirtualQueryEx(self, (LPVOID)next, &info, sizeof(info)) != 0) {\n    // extract page info\n    addr = (rword)info.BaseAddress;\n    size = info.RegionSize;\n    next = addr + size;\n\n    // skip reserved / free pages\n    if (info.State != MEM_COMMIT) {\n      continue;\n    }\n\n    // create new memory map entry\n    MemoryMap m;\n    m.range.setStart(addr);\n    m.range.setEnd(next);\n    m.permission = QBDI::PF_NONE;\n    m.permission |= PROT_ISREAD(info.Protect) ? QBDI::PF_READ : QBDI::PF_NONE;\n    m.permission |= PROT_ISWRITE(info.Protect) ? QBDI::PF_WRITE : QBDI::PF_NONE;\n    m.permission |= PROT_ISEXEC(info.Protect) ? QBDI::PF_EXEC : QBDI::PF_NONE;\n\n    // try to get current module name\n    // TODO: this is inefficient, but it was easy to implement ():)\n    DWORD ret = 0;\n    if (info.Type == MEM_IMAGE) {\n      ret = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,\n                              (LPCTSTR)addr, &mod);\n    }\n    if (ret != 0 && mod != NULL) {\n      if (full_path) {\n        ret = GetModuleFileNameEx(self, mod, path, _countof(path));\n      } else {\n        ret = GetModuleBaseName(self, mod, path, _countof(path));\n      }\n      FreeLibrary(mod);\n    }\n    if (ret != 0) {\n#ifdef UNICODE\n      std::wstring wstr(path);\n      std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;\n      m.name = conv.to_bytes(wstr);\n#else\n      m.name = std::string(path);\n#endif\n    } else {\n      // fallback to empty name\n      m.name.clear();\n    }\n    QBDI_DEBUG(\"Read new map [0x{:x}, 0x{:x}] {} {}{}{}\", m.range.start(),\n               m.range.end(), m.name.c_str(),\n               (m.permission & QBDI::PF_READ) ? \"r\" : \"-\",\n               (m.permission & QBDI::PF_WRITE) ? \"w\" : \"-\",\n               (m.permission & QBDI::PF_EXEC) ? \"x\" : \"-\");\n\n    maps.push_back(m);\n  }\n  CloseHandle(self);\n  return maps;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/MovableDoubleLinkedList.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef MovableDoubleLinkedList_H\n#define MovableDoubleLinkedList_H\n\n#include <type_traits>\n\nnamespace QBDI {\n\ntemplate <typename T>\nclass MovableDoubleLinkedList;\n\ntemplate <typename T>\nclass MovableDoubleLinkedListElement {\nprivate:\n  MovableDoubleLinkedListElement *prev;\n  MovableDoubleLinkedListElement *next;\n\n  friend MovableDoubleLinkedList<T>;\n\npublic:\n  MovableDoubleLinkedListElement() {\n    static_assert(std::is_base_of_v<MovableDoubleLinkedListElement, T>);\n    prev = this;\n    next = this;\n  }\n\n  virtual ~MovableDoubleLinkedListElement() { removeSelf(); }\n\n  MovableDoubleLinkedListElement(const MovableDoubleLinkedListElement &) =\n      delete;\n  MovableDoubleLinkedListElement &\n  operator=(const MovableDoubleLinkedListElement &) = delete;\n\nprotected:\n  MovableDoubleLinkedListElement(MovableDoubleLinkedListElement &&o) {\n    prev = o.prev;\n    next = o.next;\n    o.prev = &o;\n    o.next = &o;\n    if (prev != &o) {\n      prev->next = this;\n    } else {\n      prev = this;\n    }\n    if (next != &o) {\n      next->prev = this;\n    } else {\n      next = this;\n    }\n  }\n\n  MovableDoubleLinkedListElement &\n  operator=(MovableDoubleLinkedListElement &&o) {\n    removeSelf();\n    prev = o.prev;\n    next = o.next;\n    o.prev = &o;\n    o.next = &o;\n    if (prev != &o) {\n      prev->next = this;\n    } else {\n      prev = this;\n    }\n    if (next != &o) {\n      next->prev = this;\n    } else {\n      next = this;\n    }\n    return *this;\n  }\n\npublic:\n  void removeSelf() {\n    if (prev != this) {\n      prev->next = next;\n    }\n    if (next != this) {\n      next->prev = prev;\n    }\n    prev = this;\n    next = this;\n  }\n\n  T &getSelf() { return *static_cast<T *>(this); }\n\n  const T &getSelf() const { return *static_cast<T *>(this); }\n};\n\ntemplate <typename T>\nclass MovableDoubleLinkedList {\nprivate:\n  MovableDoubleLinkedListElement<T> listHead;\n\npublic:\n  template <bool Reverse>\n  struct Iterator {\n    Iterator(MovableDoubleLinkedListElement<T> *ptr_) : ptr(ptr_) {}\n\n    T &operator*() const { return ptr->getSelf(); }\n    T *operator->() const { return &(ptr->getSelf()); }\n    Iterator &operator++() {\n      ptr = (Reverse) ? ptr->prev : ptr->next;\n      return *this;\n    }\n    bool operator==(const Iterator &b) const { return ptr == b.ptr; };\n    bool operator!=(const Iterator &b) const { return ptr != b.ptr; };\n\n  private:\n    MovableDoubleLinkedListElement<T> *ptr;\n  };\n\n  template <bool Reverse>\n  struct ConstIterator {\n    ConstIterator(MovableDoubleLinkedListElement<T> *const ptr_) : ptr(ptr_) {}\n\n    const T &operator*() const { return ptr->getSelf(); }\n    const T *operator->() const { return &(ptr->getSelf()); }\n    ConstIterator &operator++() {\n      ptr = (Reverse) ? ptr->prev : ptr->next;\n      return *this;\n    }\n\n    bool operator==(const ConstIterator &b) const { return ptr == b.ptr; };\n    bool operator!=(const ConstIterator &b) const { return ptr != b.ptr; };\n\n  private:\n    MovableDoubleLinkedListElement<T> *const ptr;\n  };\n\n  MovableDoubleLinkedList() {}\n\n  ~MovableDoubleLinkedList() {\n    while (listHead.next != &listHead) {\n      listHead.next->removeSelf();\n    }\n  }\n\n  MovableDoubleLinkedList(MovableDoubleLinkedList &&o) = delete;\n  MovableDoubleLinkedList &operator=(MovableDoubleLinkedList &&o) = delete;\n\n  Iterator<false> begin() { return Iterator<false>(listHead.next); }\n  Iterator<false> end() { return Iterator<false>(&listHead); }\n  Iterator<true> rbegin() { return Iterator<true>(listHead.next); }\n  Iterator<true> rend() { return Iterator<true>(&listHead); }\n  ConstIterator<false> cbegin() { return ConstIterator<false>(listHead.next); }\n  ConstIterator<false> cend() { return ConstIterator<false>(&listHead); }\n  ConstIterator<true> crbegin() { return ConstIterator<true>(listHead.next); }\n  ConstIterator<true> crend() { return ConstIterator<true>(&listHead); }\n\n  void insertBegin(MovableDoubleLinkedListElement<T> &el) {\n    el.removeSelf();\n    el.next = listHead.next;\n    el.prev = &listHead;\n\n    el.next->prev = &el;\n    listHead.next = &el;\n  }\n\n  void insertEnd(MovableDoubleLinkedListElement<T> &el) {\n    el.removeSelf();\n    el.next = &listHead;\n    el.prev = listHead.prev;\n\n    el.prev->next = &el;\n    listHead.prev = &el;\n  }\n};\n\n} // namespace QBDI\n#endif // MovableDoubleLinkedList_H\n"
  },
  {
    "path": "src/Utility/StackSwitch.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"QBDI/PtrAuth.h\"\n\n#include \"Utility/StackSwitch.h\"\n\n#if defined(QBDI_PLATFORM_WINDOWS)\nextern \"C\" QBDI::rword\nqbdi_asmStackSwitch(void *switchContext, QBDI::rword newStack,\n                    QBDI::rword (*internalHandler)(void *, QBDI::rword));\n#else\nextern QBDI::rword qbdi_asmStackSwitch(\n    void *switchContext, QBDI::rword newStack,\n    QBDI::rword (*internalHandler)(void *,\n                                   QBDI::rword)) asm(\"__qbdi_asmStackSwitch\");\n#endif\n\nnamespace QBDI {\nnamespace {\n\nQBDI::rword stackSwitchInternalHandler(void *switchContext,\n                                       QBDI::rword newStackPtr) {\n  auto handler =\n      static_cast<std::function<QBDI::rword(QBDI::rword)> *>(switchContext);\n  return (*handler)(newStackPtr);\n}\n} // anonymous namespace\n\nrword switchStack(void *newStackPtr, std::function<rword(rword)> handler) {\n  auto handler_ = handler;\n  return qbdi_asmStackSwitch(&handler_, reinterpret_cast<rword>(newStackPtr),\n                             sign_code_ptrauth(&stackSwitchInternalHandler));\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/StackSwitch.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef STACKSWITCH_H\n#define STACKSWITCH_H\n\n#include <functional>\n\n#include \"QBDI/State.h\"\n\nnamespace QBDI {\n\nrword switchStack(void *newStackPtr, std::function<rword(rword)> handler);\n\n} // namespace QBDI\n\n#endif // SYSTEM_H\n"
  },
  {
    "path": "src/Utility/String.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <ctype.h>\n\n#include \"Utility/LogSys.h\"\n#include \"Utility/String.h\"\n\nnamespace QBDI {\nnamespace String {\n\nbool startsWith(const char *prefix, const char *str) {\n  QBDI_REQUIRE_ACTION(prefix != nullptr, return false);\n  QBDI_REQUIRE_ACTION(str != nullptr, return false);\n\n  while (*prefix && *str) {\n    // Wildcard matching : \"*\" until the character following the wildcard\n    // matches a character in the str\n    if (*prefix == '*') {\n      if (toupper(*(prefix + 1)) == toupper(*str++)) {\n        prefix++;\n      }\n      continue;\n    }\n    // Compare char\n    if (toupper(*prefix++) != toupper(*str++)) {\n      return false;\n    }\n  }\n  // check pending character after wildcard\n  if (*prefix && *(prefix + 1)) {\n    return false;\n  }\n  // prefix checking, if next char is not either \\0, \"_\" or [0-9] then do not\n  // match\n  if (!((*str >= '0' && *str <= '9') || *str == '_' || !*str)) {\n    return false;\n  }\n  return true;\n}\n\n} // namespace String\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/String.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef STRING_H\n#define STRING_H\n\n#include <stdbool.h>\n\nnamespace QBDI {\nnamespace String {\nbool startsWith(const char *prefix, const char *str);\n}\n} // namespace QBDI\n#endif // STRING_H\n"
  },
  {
    "path": "src/Utility/System.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef SYSTEM_H\n#define SYSTEM_H\n\n#include <stddef.h>\n#include <string>\n#include <system_error>\n#include <vector>\n\n#include \"llvm/Support/Memory.h\"\n\nnamespace QBDI {\nbool isRWXSupported();\nbool isRWRXSupported();\nllvm::sys::MemoryBlock\nallocateMappedMemory(size_t NumBytes,\n                     const llvm::sys::MemoryBlock *const NearBlock,\n                     unsigned PFlags, std::error_code &EC);\nvoid releaseMappedMemory(llvm::sys::MemoryBlock &block);\nconst std::string getHostCPUName();\nconst std::vector<std::string> getHostCPUFeatures();\nbool isHostCPUFeaturePresent(const char *f);\n} // namespace QBDI\n\n#endif // SYSTEM_H\n"
  },
  {
    "path": "src/Utility/System_generic.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string>\n#include <system_error>\n\n#include \"llvm/ADT/StringMap.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/iterator.h\"\n#include \"llvm/Support/Memory.h\"\n#include \"llvm/TargetParser/Host.h\"\n#include \"llvm/TargetParser/SubtargetFeature.h\"\n\n#include \"QBDI/Config.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\nnamespace QBDI {\n\nbool isRWXSupported() { return false; }\nbool isRWRXSupported() { return true; }\n\nllvm::sys::MemoryBlock\nallocateMappedMemory(size_t numBytes,\n                     const llvm::sys::MemoryBlock *const nearBlock,\n                     unsigned pFlags, std::error_code &ec) {\n  // forward to llvm function\n  return llvm::sys::Memory::allocateMappedMemory(numBytes, nearBlock, pFlags,\n                                                 ec);\n}\n\nvoid releaseMappedMemory(llvm::sys::MemoryBlock &block) {\n  llvm::sys::Memory::releaseMappedMemory(block);\n}\n\nconst std::string getHostCPUName() {\n  const std::string cpuname = llvm::sys::getHostCPUName().str();\n  // set default ARM CPU\n  if constexpr (is_arm)\n    if (cpuname.empty() || cpuname == \"generic\")\n      return \"cortex-a8\";\n  return cpuname;\n}\n\nconst std::vector<std::string> getHostCPUFeatures() {\n  std::vector<std::string> mattrs = {};\n  llvm::StringMap<bool> features = llvm::sys::getHostCPUFeatures();\n\n  bool ret = !features.empty();\n\n  if (!ret) {\n    QBDI_WARN(\"Fail to detect CPUHostFeatures\");\n    features.clear();\n  }\n\n  const char *fixupFeatures = getenv(\"QBDI_FIXUP_FEATURES\");\n  if (fixupFeatures != nullptr) {\n    llvm::SubtargetFeatures addFeatures(fixupFeatures);\n    for (const auto &f : addFeatures.getFeatures()) {\n      if (llvm::SubtargetFeatures::hasFlag(f)) {\n        features[llvm::SubtargetFeatures::StripFlag(f)] =\n            llvm::SubtargetFeatures::isEnabled(f);\n      } else {\n        features[f] = true;\n      }\n    }\n  }\n\n  if (ret || fixupFeatures != nullptr) {\n#if defined(_QBDI_FORCE_DISABLE_AVX)\n    const char *disable_avx = \"1\";\n#else\n    const char *disable_avx = getenv(\"QBDI_FORCE_DISABLE_AVX\");\n#endif\n\n#if defined(_QBDI_ASAN_ENABLED_)\n    const char *asan_blacklist_feature[] = {\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n        \"x87\", \"fxsr\",  \"xsave\",  \"xsaveopt\", \"xsavec\",     \"xsaves\",\n        \"sse\", \"sse2\",  \"sse3\",   \"ssse3\",    \"sse4.1\",     \"sse4.2\",\n        \"mmx\", \"3dnow\", \"3dnowa\", \"sse4a\",    \"avx\",        \"avx2\",\n        \"fma\", \"f16c\",  \"pclmul\", \"gfni\",     \"vpclmulqdq\", \"fma4\",\n        \"xop\", \"aes\",   \"vaes\",   \"sha\"\n#endif\n    };\n    const size_t asan_blacklist_feature_size =\n        sizeof(asan_blacklist_feature) / sizeof(char *);\n#endif\n\n    for (const auto &feat : features) {\n      if (!feat.getValue()) {\n        continue;\n      }\n      // XXX: #19 Bad AVX support detection in VM environments\n      // fix buggy dynamic detection\n      if (disable_avx != NULL && feat.getKey() == llvm::StringRef(\"avx\")) {\n        continue;\n      }\n#if defined(_QBDI_ASAN_ENABLED_)\n      bool blacklist_feature = false;\n      for (int i = 0; i < asan_blacklist_feature_size; i++) {\n        if (feat.getKey() == llvm::StringRef(asan_blacklist_feature[i])) {\n          blacklist_feature = true;\n          break;\n        }\n      }\n      if (blacklist_feature) {\n        continue;\n      }\n#endif\n      mattrs.push_back(feat.getKey().str());\n    }\n  }\n\n  if constexpr (is_arm) {\n    // set default ARM CPU\n    if (features.size() == 0) {\n      features.insert({\"fp16\", true});\n      features.insert({\"d16\", true});\n    }\n  }\n  // Fixing awfull LLVM API\n  if (features.count(\"fp16\") && features[\"fp16\"]) {\n    mattrs.emplace_back(\"vfp2\");\n  }\n  if (features.count(\"d16\") && features[\"d16\"]) {\n    mattrs.emplace_back(\"vfp3\");\n  }\n  return mattrs;\n}\n\nbool isHostCPUFeaturePresent(const char *query) {\n  std::vector<std::string> features = getHostCPUFeatures();\n  for (const std::string &feature : features) {\n    if (feature == query) {\n      return true;\n    }\n  }\n  return false;\n}\n\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/Version.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"QBDI/Version.h\"\n#include \"Utility/Version_commit.h\"\n\nnamespace QBDI {\n\nconst char *qbdi_getVersion(uint32_t *version) {\n  if (version != nullptr) {\n    *version = QBDI_VERSION;\n  }\n  return QBDI_VERSION_FULL_STRING;\n}\n\n}; // namespace QBDI\n"
  },
  {
    "path": "src/Utility/Version_commit.h.in",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDI_VERSION_COMMIT_H_\n#define QBDI_VERSION_COMMIT_H_\n\n#define QBDI_VERSION_FULL_STRING \"@QBDI_VERSION_FULL_STRING@\"\n\n#endif /* QBDI_VERSION_COMMIT_H_ */\n"
  },
  {
    "path": "src/Utility/X86_64/CMakeLists.txt",
    "content": "set(SOURCES \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysis_X86_64.cpp\")\n\nif(QBDI_PLATFORM_MACOS)\n  if(QBDI_ARCH_X86_64)\n    set(ASM_STUB_UTILITY \"${CMAKE_CURRENT_LIST_DIR}/macos-X86_64.s\")\n  else() # QBDI_ARCH_X86\n    set(ASM_STUB_UTILITY \"${CMAKE_CURRENT_LIST_DIR}/macos-X86.s\")\n  endif()\n  list(APPEND SOURCES \"${ASM_STUB_UTILITY}\")\n\nelseif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n\n  if(QBDI_ARCH_X86_64)\n    set(ASM_STUB_UTILITY \"${CMAKE_CURRENT_LIST_DIR}/linux-X86_64.s\")\n  else() # QBDI_ARCH_X86\n    set(ASM_STUB_UTILITY \"${CMAKE_CURRENT_LIST_DIR}/linux-X86.s\")\n  endif()\n  list(APPEND SOURCES \"${ASM_STUB_UTILITY}\")\n  set_property(SOURCE \"${ASM_STUB_UTILITY}\" PROPERTY LINK_FLAGS\n                                                     \"-Wl,-z,noexecstack\")\n\nelseif(QBDI_PLATFORM_WINDOWS)\n\n  if(QBDI_ARCH_X86_64)\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/windows-X86_64.asm\")\n  else() # QBDI_ARCH_X86\n    list(APPEND SOURCES \"${CMAKE_CURRENT_LIST_DIR}/windows-X86.asm\")\n  endif()\n\nelse()\n  message(FATAL_ERROR \"No stub for ${QBDI_PLATFORM} (${QBDI_ARCH})\")\nendif()\n\ntarget_sources(QBDI_src INTERFACE \"${SOURCES}\")\n"
  },
  {
    "path": "src/Utility/X86_64/InstAnalysis_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdlib.h>\n\n#include \"MCTargetDesc/X86BaseInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"QBDI/Bitmask.h\"\n#include \"QBDI/InstAnalysis.h\"\n#include \"QBDI/State.h\"\n#include \"Patch/Register.h\"\n#include \"Patch/Types.h\"\n#include \"Patch/X86_64/InstInfo_X86_64.h\"\n#include \"Utility/InstAnalysis_prive.h\"\n#include \"Utility/LogSys.h\"\n\nnamespace llvm {\nclass MCRegisterInfo;\n}\n\nnamespace QBDI {\nnamespace InstructionAnalysis {\n\nConditionType ConditionLLVM2QBDI(unsigned cond) {\n  switch (cond) {\n    case llvm::X86::CondCode::COND_E:\n      return CONDITION_EQUALS;\n    case llvm::X86::CondCode::COND_NE:\n      return CONDITION_NOT_EQUALS;\n    case llvm::X86::CondCode::COND_A:\n      return CONDITION_ABOVE;\n    case llvm::X86::CondCode::COND_BE:\n      return CONDITION_BELOW_EQUALS;\n    case llvm::X86::CondCode::COND_AE:\n      return CONDITION_ABOVE_EQUALS;\n    case llvm::X86::CondCode::COND_B:\n      return CONDITION_BELOW;\n    case llvm::X86::CondCode::COND_G:\n      return CONDITION_GREAT;\n    case llvm::X86::CondCode::COND_LE:\n      return CONDITION_LESS_EQUALS;\n    case llvm::X86::CondCode::COND_GE:\n      return CONDITION_GREAT_EQUALS;\n    case llvm::X86::CondCode::COND_L:\n      return CONDITION_LESS;\n    case llvm::X86::CondCode::COND_P:\n      return CONDITION_EVEN;\n    case llvm::X86::CondCode::COND_NP:\n      return CONDITION_ODD;\n    case llvm::X86::CondCode::COND_O:\n      return CONDITION_OVERFLOW;\n    case llvm::X86::CondCode::COND_NO:\n      return CONDITION_NOT_OVERFLOW;\n    case llvm::X86::CondCode::COND_S:\n      return CONDITION_SIGN;\n    case llvm::X86::CondCode::COND_NS:\n      return CONDITION_NOT_SIGN;\n    default:\n      QBDI_ABORT(\"Unsupported LLVM condition {}\", cond);\n  }\n}\n\nvoid analyseCondition(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                      const llvm::MCInstrDesc &desc, const LLVMCPU &llvmcpu) {\n  for (unsigned i = 0; i < desc.getNumOperands(); i++) {\n    const llvm::MCOperandInfo &opdesc = desc.operands()[i];\n    if (opdesc.OperandType == llvm::X86::OperandType::OPERAND_COND_CODE) {\n      instAnalysis->condition = ConditionLLVM2QBDI(\n          static_cast<unsigned>(inst.getOperand(i).getImm()));\n      return;\n    }\n  }\n  switch (inst.getOpcode()) {\n    case llvm::X86::LOOPE:\n      instAnalysis->condition = CONDITION_EQUALS;\n      break;\n    case llvm::X86::LOOPNE:\n      instAnalysis->condition = CONDITION_NOT_EQUALS;\n      break;\n    default:\n      instAnalysis->condition = CONDITION_NONE;\n      break;\n  }\n}\n\nbool isFlagOperand(unsigned opcode, unsigned opNum,\n                   const llvm::MCOperandInfo &opdesc) {\n  switch (opdesc.OperandType) {\n    default:\n      return false;\n    case llvm::X86::OperandType::OPERAND_COND_CODE:\n      return true;\n  }\n}\n\nunsigned getBias(const llvm::MCInstrDesc &desc) {\n  return llvm::X86II::getOperandBias(desc);\n}\n\nunsigned getAdditionnalOperandNumber(const llvm::MCInst &inst,\n                                     const llvm::MCInstrDesc &desc) {\n\n  if ((desc.isReturn() and isStackRead(inst)) or\n      (desc.isCall() and isStackWrite(inst))) {\n    return 1;\n  } else {\n    switch (inst.getOpcode()) {\n      case llvm::X86::LOOP:\n      case llvm::X86::LOOPE:\n      case llvm::X86::LOOPNE:\n        return 1;\n      default:\n        return 0;\n    }\n  }\n}\n\nvoid getAdditionnalOperand(InstAnalysis *instAnalysis, const llvm::MCInst &inst,\n                           const llvm::MCInstrDesc &desc,\n                           const llvm::MCRegisterInfo &MRI) {\n\n  if ((desc.isReturn() and isStackRead(inst)) or\n      (desc.isCall() and isStackWrite(inst))) {\n    // increment or decrement SP\n    OperandAnalysis &opa2 = instAnalysis->operands[instAnalysis->numOperands];\n    analyseRegister(opa2, GPR_ID[REG_SP], MRI);\n    opa2.regAccess = REGISTER_READ_WRITE;\n    opa2.flag |= OPERANDFLAG_IMPLICIT;\n    instAnalysis->numOperands++;\n    // try to merge with a previous one\n    tryMergeCurrentRegister(instAnalysis);\n  } else {\n    unsigned opcode = inst.getOpcode();\n    switch (opcode) {\n      default:\n        break;\n      case llvm::X86::LOOP:\n      case llvm::X86::LOOPE:\n      case llvm::X86::LOOPNE: {\n        // add ECX\n        OperandAnalysis &opa2 =\n            instAnalysis->operands[instAnalysis->numOperands];\n        analyseRegister(opa2, GPR_ID[2], MRI);\n        opa2.regAccess = REGISTER_READ_WRITE;\n        opa2.flag |= OPERANDFLAG_IMPLICIT;\n        instAnalysis->numOperands++;\n        // try to merge with a previous one\n        tryMergeCurrentRegister(instAnalysis);\n        break;\n      }\n    }\n  }\n}\n\n} // namespace InstructionAnalysis\n} // namespace QBDI\n"
  },
  {
    "path": "src/Utility/X86_64/linux-X86.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.hidden __qbdi_asmStackSwitch\n.globl  __qbdi_asmStackSwitch\n\n__qbdi_asmStackSwitch:\n    push ebp;\n    mov ebp, esp;\n\n    mov esp, [ebp+12];\n    and esp, ~0xf;\n    sub esp, 8;\n    push ebp;\n    push [ebp+8];\n    call [ebp+16];\n\n    mov esp, ebp;\n    pop ebp;\n    ret;\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",@progbits\n"
  },
  {
    "path": "src/Utility/X86_64/linux-X86_64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.hidden __qbdi_asmStackSwitch\n.globl  __qbdi_asmStackSwitch\n\n__qbdi_asmStackSwitch:\n    push rbp;\n    mov rbp, rsp;\n\n    mov rsp, rsi;\n    and rsp, ~0xf;\n    mov rsi, rbp;\n    call rdx;\n\n    mov rsp, rbp;\n    pop rbp;\n    ret;\n\n# mark stack as no-exec\n.section\t.note.GNU-stack,\"\",@progbits\n"
  },
  {
    "path": "src/Utility/X86_64/macos-X86.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.private_extern __qbdi_asmStackSwitch\n\n__qbdi_asmStackSwitch:\n    push ebp;\n    mov ebp, esp;\n\n    mov esp, [ebp+12];\n    and esp, ~0xf;\n    sub esp, 8;\n    push ebp;\n    push [ebp+8];\n    call [ebp+16];\n\n    mov esp, ebp;\n    pop ebp;\n    ret;\n"
  },
  {
    "path": "src/Utility/X86_64/macos-X86_64.s",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n.intel_syntax noprefix\n\n.text\n\n.private_extern __qbdi_asmStackSwitch\n\n__qbdi_asmStackSwitch:\n    push rbp;\n    mov rbp, rsp;\n\n    mov rsp, rsi;\n    and rsp, ~0xf;\n    mov rsi, rbp;\n    call rdx;\n\n    mov rsp, rbp;\n    pop rbp;\n    ret;\n"
  },
  {
    "path": "src/Utility/X86_64/windows-X86.asm",
    "content": "; This file is part of QBDI.\n;\n; Copyright 2017 - 2025 Quarkslab\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\n.386\n.xmm\n.model flat, C\n\n_TEXT segment\n\nPUBLIC qbdi_asmStackSwitch\n\n.CODE\n\nqbdi_asmStackSwitch PROC\n    push ebp;\n    mov ebp, esp;\n    mov esp, dword ptr [ebp+12];\n    and esp, -16;\n    sub esp, 8;\n    push ebp;\n    push dword ptr [ebp+8];\n    call dword ptr [ebp+16];\n    mov esp, ebp;\n    pop ebp;\n    ret;\nqbdi_asmStackSwitch ENDP\n\nEND\n"
  },
  {
    "path": "src/Utility/X86_64/windows-X86_64.asm",
    "content": "; This file is part of QBDI.\n;\n; Copyright 2017 - 2025 Quarkslab\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\nPUBLIC qbdi_asmStackSwitch\n\n.CODE\n\nqbdi_asmStackSwitch PROC\n    push rbp;\n    mov rbp, rsp;\n    mov rsp, rdx;\n    and rsp, -16;\n    mov rdx, rbp;\n    call r8;\n    mov rsp, rbp;\n    pop rbp;\n    ret;\nqbdi_asmStackSwitch ENDP\n\nEND\n"
  },
  {
    "path": "src/devVariable.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef DEVVARIABLE_H\n#define DEVVARIABLE_H\n\n#include \"QBDI/Config.h\"\n\n#ifdef QBDI_LOG_DEBUG\n#define DEBUG_INST_OPERAND 1\n#define CHECK_INSTRUCTION_SIZE 1\n#define CHECK_MEMORYACCESS_TABLE 1\n#define CHECK_INSTINFO_TABLE 1\n#define CHECK_NUM_OPERANDS 1\n#else\n#define DEBUG_INST_OPERAND 0\n#define CHECK_INSTRUCTION_SIZE 0\n#define CHECK_MEMORYACCESS_TABLE 0\n#define CHECK_INSTINFO_TABLE 0\n#define CHECK_NUM_OPERANDS 0\n#endif\n\n#endif // DEVVARIABLE_H\n"
  },
  {
    "path": "src/fridaStubs.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stddef.h>\n#include <stdint.h>\n\n#include <QBDI.h>\n\nnamespace QBDI {\nextern \"C\" {\n\nQBDI_EXPORT rword qbdi_getGPR(GPRState *state, uint32_t rid) {\n  return QBDI_GPR_GET(state, rid);\n}\n\nQBDI_EXPORT void qbdi_setGPR(GPRState *state, uint32_t rid, rword val) {\n  QBDI_GPR_SET(state, rid, val);\n}\n\nstruct StructDesc {\n  uint32_t size;\n  uint32_t items;\n  uint32_t offsets[30];\n};\n\nQBDI_EXPORT const StructDesc *qbdi_getMemoryAccessStructDesc() {\n  static const StructDesc MemoryAccessDesc{\n      sizeof(MemoryAccess),\n      6,\n      {\n          offsetof(MemoryAccess, instAddress),\n          offsetof(MemoryAccess, accessAddress),\n          offsetof(MemoryAccess, value),\n          offsetof(MemoryAccess, size),\n          offsetof(MemoryAccess, type),\n          offsetof(MemoryAccess, flags),\n      }};\n\n  return &MemoryAccessDesc;\n}\n\nQBDI_EXPORT const StructDesc *qbdi_getVMStateStructDesc() {\n  static const StructDesc VMStateDesc{sizeof(VMState),\n                                      6,\n                                      {\n                                          offsetof(VMState, event),\n                                          offsetof(VMState, sequenceStart),\n                                          offsetof(VMState, sequenceEnd),\n                                          offsetof(VMState, basicBlockStart),\n                                          offsetof(VMState, basicBlockEnd),\n                                          offsetof(VMState, lastSignal),\n                                      }};\n  return &VMStateDesc;\n}\n\nQBDI_EXPORT const StructDesc *qbdi_getOperandAnalysisStructDesc() {\n  static const StructDesc OperandAnalysisDesc{\n      sizeof(OperandAnalysis),\n      8,\n      {\n          offsetof(OperandAnalysis, type),\n          offsetof(OperandAnalysis, flag),\n          offsetof(OperandAnalysis, value),\n          offsetof(OperandAnalysis, size),\n          offsetof(OperandAnalysis, regOff),\n          offsetof(OperandAnalysis, regCtxIdx),\n          offsetof(OperandAnalysis, regName),\n          offsetof(OperandAnalysis, regAccess),\n      }};\n  return &OperandAnalysisDesc;\n}\n\nQBDI_EXPORT const StructDesc *qbdi_getInstAnalysisStructDesc() {\n  static const StructDesc InstAnalysisDesc{\n      sizeof(InstAnalysis),\n      28,\n      {\n          offsetof(InstAnalysis, mnemonic),\n          offsetof(InstAnalysis, disassembly),\n          offsetof(InstAnalysis, address),\n          offsetof(InstAnalysis, instSize),\n          offsetof(InstAnalysis, affectControlFlow),\n          offsetof(InstAnalysis, isBranch),\n          offsetof(InstAnalysis, isCall),\n          offsetof(InstAnalysis, isReturn),\n          offsetof(InstAnalysis, isCompare),\n          offsetof(InstAnalysis, isPredicable),\n          offsetof(InstAnalysis, isMoveImm),\n          offsetof(InstAnalysis, mayLoad),\n          offsetof(InstAnalysis, mayStore),\n          offsetof(InstAnalysis, loadSize),\n          offsetof(InstAnalysis, storeSize),\n          offsetof(InstAnalysis, condition),\n          offsetof(InstAnalysis, flagsAccess),\n          offsetof(InstAnalysis, numOperands),\n          offsetof(InstAnalysis, operands),\n          offsetof(InstAnalysis, symbolName),\n          offsetof(InstAnalysis, symbolOffset),\n          offsetof(InstAnalysis, moduleName),\n          offsetof(InstAnalysis, cpuMode),\n          offsetof(InstAnalysis, patchAddress),\n          offsetof(InstAnalysis, patchSize),\n          offsetof(InstAnalysis, patchInstOffset),\n          offsetof(InstAnalysis, patchInstSize),\n          offsetof(InstAnalysis, analysisType),\n      }};\n  return &InstAnalysisDesc;\n}\n\n} // extern \"C\"\n} // namespace QBDI\n"
  },
  {
    "path": "src/windowsDllMain.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stddef.h>\n#include <windows.h>\n\n#include \"QBDI/Platform.h\"\n\nQBDI_EXPORT BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason,\n                                   IN LPVOID Reserved) {\n  switch (nReason) {\n    case DLL_PROCESS_ATTACH:\n      //  For optimization.\n      DisableThreadLibraryCalls(hDllHandle);\n      break;\n    case DLL_PROCESS_DETACH:\n      break;\n  }\n  return TRUE;\n}\n"
  },
  {
    "path": "templates/qbdi_frida_template/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.2)\nproject(qbdi-frida-template)\nadd_executable(demo.bin SimpleXOR.c)\n"
  },
  {
    "path": "templates/qbdi_frida_template/FridaQBDI_sample.js",
    "content": "// QBDI\nimport { VM, InstPosition, VMAction } from \"./frida-qbdi.js\";\n\n// Initialize QBDI\nvar vm = new VM();\nvar state = vm.getGPRState();\nvar stack = vm.allocateVirtualStack(state, 0x100000);\n\n// Instrument \"Secret\" function from demo.bin\nvar funcPtr = Module.findGlobalExportByName(\"Secret\");\nif (!funcPtr) {\n    funcPtr = DebugSymbol.fromName(\"Secret\").address;\n}\nvm.addInstrumentedModuleFromAddr(funcPtr);\n\n// Callback on every instruction\n// This callback will print context and display current instruction address and dissassembly\n// We choose to print only XOR instructions\nvar icbk = vm.newInstCallback(function(vm, gpr, fpr, data) {\n    var inst = vm.getInstAnalysis();\n    if (inst.mnemonic.search(\"XOR\")){\n        return VMAction.CONTINUE;\n    }\n    gpr.dump(); // Display context\n    console.log(\"0x\" + inst.address.toString(16) + \" \" + inst.disassembly); // Display instruction dissassembly\n    return VMAction.CONTINUE;\n});\nvar iid = vm.addCodeCB(InstPosition.PREINST, icbk);\n\n// Allocate a string in remote process memory\nvar strP = Memory.allocUtf8String(\"Hello world !\");\n// Call the Secret function using QBDI and with our string as argument\nvm.call(funcPtr, [strP]);\n"
  },
  {
    "path": "templates/qbdi_frida_template/SimpleXOR.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#if defined(_MSC_VER)\n#define EXPORT __declspec(dllexport)\n#else // _MSC_VER\n#define EXPORT __attribute__((visibility(\"default\")))\n#endif\n\nEXPORT int Secret(char *str) {\n  int i;\n  unsigned char XOR[] = {0x51, 0x42, 0x44, 0x49, 0x46, 0x72, 0x69, 0x64, 0x61};\n  size_t len = strlen(str);\n\n  printf(\"Input string is : %s\\nEncrypted string is : \\n\", str);\n\n  for (i = 0; i < len; i++) {\n    printf(\"0x%x,\", str[i] ^ XOR[i % sizeof(XOR)]);\n  }\n  printf(\"\\n\");\n  fflush(stdout);\n  return 0;\n}\n\nvoid Hello() { Secret(\"Hello world !\"); }\n\nint main() { Hello(); }\n"
  },
  {
    "path": "templates/qbdi_preload_template/CMakeLists.txt.in",
    "content": "cmake_minimum_required (VERSION 3.2)\nproject(QBDITemplate)\n\nfind_package(QBDI@QBDI_ARCH@ REQUIRED)\nfind_package(QBDIPreload@QBDI_ARCH@ REQUIRED)\n\nadd_library(qbdi_tracer SHARED qbdi_preload_template.c)\ntarget_link_libraries(qbdi_tracer QBDIPreload::@QBDI_ARCH@::QBDIPreload QBDI::@QBDI_ARCH@::QBDI_static)\n"
  },
  {
    "path": "templates/qbdi_preload_template/README.txt",
    "content": "Introduction\n============\n\nA simple template to easily create an instrumentation tool based\non libQBDIPreload.\n\n\nCompilation\n===========\n\nLinux / macOS\n-------------\n\n$ # unix\n$ mkdir build\n$ cd build\n$ cmake ..\n$ make\n\nUsage\n=====\n\nmacOS\n-----\n\nsudo DYLD_INSERT_LIBRARIES=./libqbdi_tracer.dylib ./mytarget\n\nLinux\n-----\n\nLD_PRELOAD=./libqbdi_tracer.so ./mytarget\n"
  },
  {
    "path": "templates/qbdi_preload_template/qbdi_preload_template.c",
    "content": "#include <stdio.h>\n#include \"QBDIPreload.h\"\n\nQBDIPRELOAD_INIT;\n\nstatic VMAction onInstruction(VMInstanceRef vm, GPRState *gprState,\n                              FPRState *fprState, void *data) {\n  const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(\n      vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY |\n              QBDI_ANALYSIS_SYMBOL);\n  if (instAnalysis->symbolName != NULL) {\n    printf(\"%20s+%05u\\t\", instAnalysis->symbolName, instAnalysis->symbolOffset);\n  } else {\n    printf(\"%26s\\t\", \"\");\n  }\n  printf(\"0x%\" PRIRWORD \" %s\\n\", instAnalysis->address,\n         instAnalysis->disassembly);\n  return QBDI_CONTINUE;\n}\n\nint qbdipreload_on_start(void *main) { return QBDIPRELOAD_NOT_HANDLED; }\n\nint qbdipreload_on_premain(void *gprCtx, void *fpuCtx) {\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_main(int argc, char **argv) {\n  if (getenv(\"QBDI_DEBUG\") != NULL) {\n    qbdi_setLogPriority(QBDI_DEBUG);\n  } else {\n    qbdi_setLogPriority(QBDI_WARNING);\n  }\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint qbdipreload_on_run(VMInstanceRef vm, rword start, rword stop) {\n  qbdi_addCodeCB(vm, QBDI_PREINST, onInstruction, NULL, 0);\n  qbdi_run(vm, start, stop);\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint qbdipreload_on_exit(int status) { return QBDIPRELOAD_NO_ERROR; }\n"
  },
  {
    "path": "templates/qbdi_template/CMakeLists.txt.in",
    "content": "cmake_minimum_required (VERSION 3.2)\nproject(QBDITemplate)\n\nfind_package(QBDI@QBDI_ARCH@ REQUIRED)\n\nadd_executable(qbdi_template qbdi_template.c)\n\n# Easier to build using QBDI static library on Windows\nif(MSVC)\n  target_link_libraries(qbdi_template QBDI::@QBDI_ARCH@::QBDI_static)\nelse()\n  target_link_libraries(qbdi_template QBDI::@QBDI_ARCH@::QBDI)\nendif()\n"
  },
  {
    "path": "templates/qbdi_template/README.txt",
    "content": "Introduction\n============\n\nA simple template to easily create an instrumentation tool\nlinking QBDI at compile time.\n\n\nCompilation\n===========\n\nLinux / macOS\n-------------\n\n$ # unix\n$ mkdir build\n$ cd build\n$ cmake ..\n$ make\n\n\nWindows\n-------\n\n$ # win\n$ mkdir build\n$ cd build\n$ cmake -G \"Visual Studio 14 2015 Win64\" -DCMAKE_BUILD_TYPE=Release ..\n$ MSBuild.exe /p:Configuration=Release ALL_BUILD.vcxproj\n\nIf compilation is successful, test binary will be in Release directory.\nIf compiled dynamically, it will need QBDI.dll (from lib directory).\n\nNote 1: Visual Studio version must match the one installed on your system.\nNote 2: under cygwin, QBDI.dll needs to have executable bit set.\n"
  },
  {
    "path": "templates/qbdi_template/qbdi_template.c",
    "content": "#include <assert.h>\n#include <stdbool.h>\n#include <stdio.h>\n\n#include <QBDI.h>\n\n#define STACK_SIZE 0x100000\n\nQBDI_NOINLINE int secretFunc(unsigned int value) { return value ^ 0x5c; }\n\nVMAction showInstruction(VMInstanceRef vm, GPRState *gprState,\n                         FPRState *fprState, void *data) {\n  // Obtain an analysis of the instruction from the VM\n  const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(\n      vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);\n  // Printing disassembly\n  printf(\"0x%\" PRIRWORD \": %s\\n\", instAnalysis->address,\n         instAnalysis->disassembly);\n  return QBDI_CONTINUE;\n}\n\nint main(int argc, char **argv) {\n  VMInstanceRef vm = NULL;\n  uint8_t *fakestack = NULL;\n\n  // init VM\n  qbdi_initVM(&vm, NULL, NULL, 0);\n\n  // Get a pointer to the GPR state of the VM\n  GPRState *state = qbdi_getGPRState(vm);\n  assert(state != NULL);\n\n  // Setup initial GPR state, this fakestack will produce a ret NULL at the end\n  // of the execution\n  bool res = qbdi_allocateVirtualStack(state, STACK_SIZE, &fakestack);\n  assert(res == true);\n\n  // Add callback on our instruction range\n  uint32_t uid =\n      qbdi_addCodeRangeCB(vm, (rword)&secretFunc, (rword)&secretFunc + 100,\n                          QBDI_PREINST, showInstruction, vm, 0);\n  assert(uid != QBDI_INVALID_EVENTID);\n\n  // add executable code range\n  res = qbdi_addInstrumentedModuleFromAddr(vm, (rword)&main);\n  assert(res == true);\n\n  // call secretFunc using VM, custom state and fake stack\n  // eq: secretFunc(666);\n  rword retval;\n  res = qbdi_call(vm, &retval, (rword)secretFunc, 1, 666);\n  assert(res == true);\n\n  // get return value from current state\n  printf(\"[*] retval=0x%\" PRIRWORD \"\\n\", retval);\n\n  // free everything\n  qbdi_alignedFree(fakestack);\n  qbdi_terminateVM(vm);\n\n  return 0;\n}\n"
  },
  {
    "path": "test/API/AARCH64/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysisTest_AARCH64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest_AARCH64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/VMTest_AARCH64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/VMTest_AARCH64_LocalMonitor.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/VMTest_AARCH64_X28.cpp\")\n\nif(NOT (QBDI_PLATFORM_MACOS OR QBDI_PLATFORM_IOS))\n  target_sources(QBDITest\n                 PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/VMTest_AARCH64_LDRL.cpp\")\nendif()\n"
  },
  {
    "path": "test/API/AARCH64/InstAnalysisTest_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n\nstruct ExpectedInstAnalysis {\n  std::string mnemonic;\n  QBDI::rword address;\n  uint32_t instSize;\n  bool affectControlFlow;\n  bool isBranch;\n  bool isCall;\n  bool isReturn;\n  bool isCompare;\n  bool isPredicable;\n  bool mayLoad;\n  bool mayStore;\n  uint32_t loadSize;\n  uint32_t storeSize;\n  QBDI::ConditionType condition;\n};\n\n[[maybe_unused]] static void debugOperand(const QBDI::InstAnalysis *ana) {\n  if ((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n      QBDI::ANALYSIS_OPERANDS) {\n    for (int i = 0; i < ana->numOperands; i++) {\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      WARN(\"- type: \" << op.type << \", flag: \" << op.flag\n                      << \", value: \" << op.value << \", size: \" << (int)op.size\n                      << \", regOff : \" << (int)op.regOff\n                      << \", regCtxIdx: \" << op.regCtxIdx << \", regName: \"\n                      << (op.regName == nullptr ? \"nullptr\" : op.regName)\n                      << \", regAccess: \"\n                      << (op.regAccess & QBDI::REGISTER_READ ? \"r\" : \"-\")\n                      << (op.regAccess & QBDI::REGISTER_WRITE ? \"w\" : \"-\"));\n    }\n  }\n}\n\nstatic void checkOperand(const QBDI::InstAnalysis *ana,\n                         const std::vector<QBDI::OperandAnalysis> expecteds,\n                         QBDI::RegisterAccessType flagsAccess) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n             QBDI::ANALYSIS_OPERANDS) {\n    CHECK(flagsAccess == ana->flagsAccess);\n    CHECK(expecteds.size() == ana->numOperands);\n    for (unsigned i = 0;\n         i < std::min<unsigned>(ana->numOperands, expecteds.size()); i++) {\n      const QBDI::OperandAnalysis &expect = expecteds[i];\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      INFO(\"For operand \" << i);\n\n      CHECK(expect.type == op.type);\n      CHECK(expect.flag == op.flag);\n      if (op.type == QBDI::OPERAND_IMM || expect.value != 0) {\n        CHECK(expect.value == op.value);\n      }\n      CHECK(expect.size == op.size);\n      CHECK(expect.regOff == op.regOff);\n      CHECK(expect.regCtxIdx == op.regCtxIdx);\n      CHECK(expect.regAccess == op.regAccess);\n\n      const std::string expectedRegName(\n          (expect.regName != nullptr) ? expect.regName : \"\");\n      const std::string foundRegName((op.regName != nullptr) ? op.regName : \"\");\n      CHECK(expectedRegName == foundRegName);\n\n      if (expect.regName == nullptr || op.regName == nullptr) {\n        CHECK(expect.regName == op.regName);\n      }\n    }\n  }\n}\n\nstatic void checkInst(const QBDI::InstAnalysis *ana,\n                      const ExpectedInstAnalysis expected) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_INSTRUCTION) ==\n             QBDI::ANALYSIS_INSTRUCTION) {\n    CHECK(expected.mnemonic == ana->mnemonic);\n    CHECK(expected.address == ana->address);\n    CHECK(expected.instSize == ana->instSize);\n    CHECK(expected.affectControlFlow == ana->affectControlFlow);\n    CHECK(expected.isBranch == ana->isBranch);\n    CHECK(expected.isCall == ana->isCall);\n    CHECK(expected.isReturn == ana->isReturn);\n    CHECK(expected.isCompare == ana->isCompare);\n    CHECK(expected.isPredicable == ana->isPredicable);\n    CHECK(expected.mayLoad == ana->mayLoad);\n    CHECK(expected.mayStore == ana->mayStore);\n    CHECK(expected.loadSize == ana->loadSize);\n    CHECK(expected.storeSize == ana->storeSize);\n    CHECK(expected.condition == ana->condition);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-CachedInst\") {\n\n  QBDI::rword addr = genASM(\"stp x0, x1, [sp]\\n\");\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n\n  vm.clearAllCache();\n\n  CHECK(vm.getCachedInstAnalysis(addr) == nullptr);\n\n  vm.precacheBasicBlock(addr);\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-ret\") {\n\n  QBDI::rword addr = genASM(\"ret\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"RET\", addr,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ true, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 30,\n                    \"LR\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-br\") {\n\n  QBDI::rword addr = genASM(\"br x0\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"BR\", addr,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"X0\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-blr\") {\n\n  QBDI::rword addr = genASM(\"blr x10\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"BLR\", addr,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 10,\n                    \"X10\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 31,\n                    \"SP\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 30,\n                    \"LR\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-b\") {\n\n  QBDI::rword addr = genASM(\"b label\\nnop\\nnop\\nnop\\nlabel: nop\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"B\", addr,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 4, 2, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-bl\") {\n\n  QBDI::rword addr = genASM(\"bl label\\nnop\\nnop\\nnop\\nlabel: nop\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"BL\", addr,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 4, 2, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 31,\n                    \"SP\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 30,\n                    \"LR\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-addi\") {\n\n  QBDI::rword addr = genASM(\"add x17, X28, #258\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADDXri\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 17,\n                    \"X17\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 28,\n                    \"X28\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 258,\n                    8, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-addr\") {\n\n  QBDI::rword addr = genASM(\"add W17, W28, W8, LSR #8\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADDWrs\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 17,\n                    \"W17\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 28,\n                    \"W28\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 8, \"W8\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0x48,\n                    8, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-subs\") {\n\n  QBDI::rword addr = genASM(\"subs X17, X28, X8\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"SUBSXrs\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 17,\n                    \"X17\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 28,\n                    \"X28\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 8, \"X8\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-cmp\") {\n\n  QBDI::rword addr = genASM(\"cmp X28, X8\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"SUBSXrs\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_NONE, 0, 8, 0, -1,\n                    \"XZR\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 28,\n                    \"X28\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 8, \"X8\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-adr\") {\n\n  QBDI::rword addr = genASM(\"adr X28, #127\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADR\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 28,\n                    \"X28\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 127, 2, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-cbz\") {\n\n  QBDI::rword addr = genASM(\"cbz x28, label\\nnop\\nnop\\nnop\\nlabel: nop\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CBZX\", addr,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 28,\n                    \"X28\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 4, 2, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-bcc\") {\n\n  QBDI::rword addr = genASM(\"b.le label\\nnop\\nnop\\nnop\\nlabel: nop\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"Bcc\", addr,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_LESS_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 4, 2, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-ldp\") {\n\n  QBDI::rword addr = genASM(\"ldp x0, x1, [x2, #8]\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDPXi\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 16, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"X0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1, \"X1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2, \"X2\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 1, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-ldp_pre\") {\n\n  QBDI::rword addr = genASM(\"ldp x0, x1, [x2, #8]!\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDPXpre\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 16, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"X0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1, \"X1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2, \"X2\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 1, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-ldp_post\") {\n\n  QBDI::rword addr = genASM(\"ldp x0, x1, [x2], #8\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDPXpost\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 16, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"X0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1, \"X1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2, \"X2\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 1, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-st1Three\") {\n\n  QBDI::rword addr = genASM(\"st1  { v0.8b, v1.8b, v2.8b }, [x0], #24\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ST1Threev8b_POST\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 24,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"D0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 16,\n                    \"D1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 32,\n                    \"D2\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"X0\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_NONE, 0, 8, 0, -1,\n                    \"XZR\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_AARCH64-ld4\") {\n\n  QBDI::rword addr = genASM(\"ld4  { v0.b, v1.b, v2.b, v3.b }[3], [x0], #4\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LD4i8_POST\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 0,\n                    \"Q0\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 16,\n                    \"Q1\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 32,\n                    \"Q2\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 48,\n                    \"Q3\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 3, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"X0\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_NONE, 0, 8, 0, -1,\n                    \"XZR\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n"
  },
  {
    "path": "test/API/AARCH64/MemoryAccessTest_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#include \"Utility/System.h\"\n\nstatic bool checkFeature(const char *f) {\n  if (!QBDI::isHostCPUFeaturePresent(f)) {\n    WARN(\"Host doesn't support \" << f << \" feature: SKIP\");\n    return false;\n  }\n  return true;\n}\n\n[[maybe_unused]] static QBDI::VMAction debugCB(QBDI::VMInstanceRef vm,\n                                               QBDI::GPRState *gprState,\n                                               QBDI::FPRState *fprState,\n                                               void *data) {\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  printf(\"0x%lx (%10s): %s\\n\", instAnalysis->address, instAnalysis->mnemonic,\n         instAnalysis->disassembly);\n\n  for (auto &a : vm->getInstMemoryAccess()) {\n    printf(\n        \" - inst: 0x%lx, addr: 0x%lx, size: %d, type: %c%c, \"\n        \"value: 0x%lx, flags : 0x %x\\n\",\n        a.instAddress, a.accessAddress, a.size,\n        ((a.type & QBDI::MEMORY_READ) != 0) ? 'r' : '-',\n        ((a.type & QBDI::MEMORY_WRITE) != 0) ? 'w' : '-', a.value, a.flags);\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nstruct ExpectedMemoryAccess {\n  QBDI::rword address;\n  QBDI::rword value;\n  uint16_t size;\n  QBDI::MemoryAccessType type;\n  QBDI::MemoryAccessFlags flags;\n  bool see = false;\n};\n\nstruct ExpectedMemoryAccesses {\n  std::vector<ExpectedMemoryAccess> accesses;\n};\n\nstatic QBDI::VMAction checkAccess(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n\n  ExpectedMemoryAccesses *info = static_cast<ExpectedMemoryAccesses *>(data);\n  if (std::all_of(info->accesses.begin(), info->accesses.end(),\n                  [](ExpectedMemoryAccess &a) { return a.see; }))\n    return QBDI::VMAction::CONTINUE;\n\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n\n  CHECKED_IF(memaccesses.size() == info->accesses.size()) {\n    for (size_t i = 0; i < info->accesses.size(); i++) {\n      auto &memaccess = memaccesses[i];\n      auto &expect = info->accesses[i];\n      INFO(\"Expected Access n°\" << i);\n      INFO(\"Value 0x\" << std::hex << memaccess.value << \" expect 0x\" << std::hex\n                      << expect.value);\n      CHECKED_IF(memaccess.accessAddress == expect.address)\n      CHECKED_IF((memaccess.value == expect.value || expect.value == 0))\n      CHECKED_IF(memaccess.size == expect.size)\n      CHECKED_IF(memaccess.type == expect.type)\n      CHECKED_IF(memaccess.flags == expect.flags)\n      expect.see = true;\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nvoid setFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword hvalue,\n            QBDI::rword lvalue) {\n  reinterpret_cast<QBDI::rword *>(fpr)[index * 2] = lvalue;\n  reinterpret_cast<QBDI::rword *>(fpr)[index * 2 + 1] = hvalue;\n}\n\nvoid checkFullFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword hvalue,\n                  QBDI::rword lvalue) {\n  INFO(\"v\" << index);\n  CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2] == lvalue);\n  CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2 + 1] == hvalue);\n}\n\nvoid checkLowFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword lvalue) {\n  INFO(\"v\" << index);\n  CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2] == lvalue);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldpx\") {\n\n  const char source[] = \"ldp x0, x1, [x2]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDPXi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == v1[0]);\n  CHECK(state->x1 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldpx_2\") {\n\n  const char source[] = \"ldp x0, x1, [x2, #8]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull,\n                      0x616d087f1e054a7cull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDPXi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == v1[1]);\n  CHECK(state->x1 == v1[2]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldpx_pre\") {\n\n  const char source[] = \"ldp x0, x1, [x2, #8]!\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull,\n                      0x616d087f1e054a7cull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDPXpre\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == v1[1]);\n  CHECK(state->x1 == v1[2]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldpx_post\") {\n\n  const char source[] = \"ldp x0, x1, [x2], #8\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull,\n                      0x616d087f1e054a7cull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDPXpost\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == v1[0]);\n  CHECK(state->x1 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldpw\") {\n\n  const char source[] = \"ldp w0, w1, [x2]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDPWi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == (v1[0] & 0xffffffff));\n  CHECK(state->x1 == (v1[0] >> 32));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldpw_2\") {\n\n  const char source[] = \"ldp w0, w1, [x2, #8]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bul};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDPWi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == (v1[1] & 0xffffffff));\n  CHECK(state->x1 == (v1[1] >> 32));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldpw_pre\") {\n\n  const char source[] = \"ldp w0, w1, [x2, #8]!\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bul};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDPWpre\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == (v1[1] & 0xffffffff));\n  CHECK(state->x1 == (v1[1] >> 32));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrb_1\") {\n\n  const char source[] = \"ldrb w0, [x1, x2]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[1], v1[1] & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBBroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 8;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == (v1[1] & 0xff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrb_2\") {\n\n  const char source[] = \"ldrb w0, [x1, x2]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBBroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 32) & 0xff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrb_3\") {\n\n  const char source[] = \"ldrb w0, [x1, w2, SXTW]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBBroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 32) & 0xff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrb_4\") {\n\n  const char source[] = \"ldrb w0, [x1, w2, UXTW]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 3, (v1[0] >> 24) & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBBroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 3;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 24) & 0xff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrb_5\") {\n\n  const char source[] = \"ldrb w2, [x1, w2, SXTW]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBBroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = 0xfffffffc;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x2 == ((v1[0] >> 32) & 0xff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_1\") {\n\n  const char source[] = \"ldrh w0, [x1, x2, LSL #0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 5, (v1[0] >> 40) & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 5;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 40) & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_2\") {\n\n  const char source[] = \"ldrh w0, [x1, x2, LSL #1]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[1]) + 2, (v1[1] >> 16) & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 5;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[1] >> 16) & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_3\") {\n\n  const char source[] = \"ldrh w0, [x1, x2, SXTX #0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 32) & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_4\") {\n\n  const char source[] = \"ldrh w0, [x1, x2, SXTX #1]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0] & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == (v1[0] & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_5\") {\n\n  const char source[] = \"ldrh w0, [x1, w2, UXTW #0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 5, (v1[0] >> 40) & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 5;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 40) & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_6\") {\n\n  const char source[] = \"ldrh w0, [x1, w2, UXTW #1]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[1]) + 2, (v1[1] >> 16) & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 5;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[1] >> 16) & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_7\") {\n\n  const char source[] = \"ldrh w0, [x1, w2, SXTW #0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 32) & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrh_8\") {\n\n  const char source[] = \"ldrh w0, [x1, w2, SXTW #1]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0] & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRHHroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == (v1[0] & 0xffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrw_1\") {\n\n  const char source[] = \"ldr w0, [x1, x2, LSL #0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 3, (v1[0] >> 24) & 0xffffffff, 4,\n       QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRWroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 3;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 24) & 0xffffffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrw_2\") {\n\n  const char source[] = \"ldr w0, [x1, x2, LSL #2]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull,\n                      0x2c9f7f51fb7d40ceull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[2]) + 4, (v1[2] >> 32) & 0xffffffff, 4,\n       QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRWroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  state->x2 = 5;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[2] >> 32) & 0xffffffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrw_3\") {\n\n  const char source[] = \"ldr w0, [x1, x2, SXTX #0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xffffffff, 4,\n       QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRWroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 32) & 0xffffffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrw_4\") {\n\n  const char source[] = \"ldr w0, [x1, x2, SXTX #2]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0] & 0xffffffff, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRWroX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 16;\n  state->x2 = -4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == (v1[0] & 0xffffffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrxui\") {\n\n  const char source[] = \"ldr x0, [x1, #8]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRXui\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrxpre\") {\n\n  const char source[] = \"ldr x0, [x1, #8]!\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRXpre\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrwui\") {\n\n  const char source[] = \"ldr w0, [x1, #4]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xffffffff, 4,\n       QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRWui\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 32) & 0xffffffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldr_sxtw\") {\n\n  const char source[] = \"ldr x3, [x1, w2, SXTW#3]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull,\n                      0x54869574235eab1eull, 0x13709731a1fe1cb4ull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[1]), v1[1], 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRXroW\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1) + 24;\n  state->x2 = 0xfffffffe;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x3 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrwpre\") {\n\n  const char source[] = \"ldr w0, [x1, #4]!\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x8e060b1505409a1bull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {((QBDI::rword)&v1[0]) + 4, (v1[0] >> 32) & 0xffffffff, 4,\n       QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRWpre\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 0;\n  state->x1 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == ((v1[0] >> 32) & 0xffffffff));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldrxl\") {\n\n  const char source[] =\n      \"  ldr x0, label\\n\"\n      \"  b target\\n\"\n      \"label:\\n\"\n      \"  brk 65535\\n\"\n      \"  brk 65535\\n\"\n      \"target:\\n\"\n      \"  ret\\n\";\n\n  QBDI::rword addr = genASM(source);\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {addr + 8, 0xD43FFFE0D43FFFE0ull, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRXl\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, addr);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->x0 == 0xD43FFFE0D43FFFE0ull);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1rv1d\") {\n\n  const char source[] = \"ld1r  { v0.1d }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Rv1d\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1rv2d\") {\n\n  const char source[] = \"ld1r  { v0.2d }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Rv2d\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkFullFPR(fstate, 0, v1, v1);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1rv2s\") {\n\n  const char source[] = \"ld1r  { v0.2s }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xffffffff, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Rv2s\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, (v1 & 0xffffffff) | ((v1 & 0xffffffff) << 32));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1rv4h\") {\n\n  const char source[] = \"ld1r  { v0.4h }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Rv4h\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev = v1 & 0xffff;\n  ev |= (ev << 16);\n  ev |= (ev << 32);\n  checkLowFPR(fstate, 0, ev);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1rv8b\") {\n\n  const char source[] = \"ld1r  { v0.8b }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Rv8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev = v1 & 0xff;\n  ev |= (ev << 8);\n  ev |= (ev << 16);\n  ev |= (ev << 32);\n  checkLowFPR(fstate, 0, ev);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld2rv16b\") {\n\n  const char source[] = \"ld2r  { v0.16b, v1.16b }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xffff, 2, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD2Rv16b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev1 = v1 & 0xff;\n  ev1 |= (ev1 << 8);\n  ev1 |= (ev1 << 16);\n  ev1 |= (ev1 << 32);\n  checkFullFPR(fstate, 0, ev1, ev1);\n  QBDI::rword ev2 = (v1 >> 8) & 0xff;\n  ev2 |= (ev2 << 8);\n  ev2 |= (ev2 << 16);\n  ev2 |= (ev2 << 32);\n  checkFullFPR(fstate, 1, ev2, ev2);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld3rv16b\") {\n\n  const char source[] = \"ld3r  { v0.16b, v1.16b, v2.16b }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xffffff, 3, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD3Rv16b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev1 = v1 & 0xff;\n  ev1 |= (ev1 << 8);\n  ev1 |= (ev1 << 16);\n  ev1 |= (ev1 << 32);\n  checkFullFPR(fstate, 0, ev1, ev1);\n  QBDI::rword ev2 = (v1 >> 8) & 0xff;\n  ev2 |= (ev2 << 8);\n  ev2 |= (ev2 << 16);\n  ev2 |= (ev2 << 32);\n  checkFullFPR(fstate, 1, ev2, ev2);\n  QBDI::rword ev3 = (v1 >> 16) & 0xff;\n  ev3 |= (ev3 << 8);\n  ev3 |= (ev3 << 16);\n  ev3 |= (ev3 << 32);\n  checkFullFPR(fstate, 2, ev3, ev3);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld3rv8h\") {\n\n  const char source[] = \"ld3r  { v0.8h, v1.8h, v2.8h }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xffffffffffff, 6, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD3Rv8h\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev1 = v1 & 0xffff;\n  ev1 |= (ev1 << 16);\n  ev1 |= (ev1 << 32);\n  checkFullFPR(fstate, 0, ev1, ev1);\n  QBDI::rword ev2 = (v1 >> 16) & 0xffff;\n  ev2 |= (ev2 << 16);\n  ev2 |= (ev2 << 32);\n  checkFullFPR(fstate, 1, ev2, ev2);\n  QBDI::rword ev3 = (v1 >> 32) & 0xffff;\n  ev3 |= (ev3 << 16);\n  ev3 |= (ev3 << 32);\n  checkFullFPR(fstate, 2, ev3, ev3);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld3rv4s\") {\n\n  const char source[] = \"ld3r  { v0.4s, v1.4s, v2.4s }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x0a18ce5402b84b8aull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1] & 0xffffffff, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD3Rv4s\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev1 = v1[0] & 0xffffffff;\n  ev1 |= (ev1 << 32);\n  checkFullFPR(fstate, 0, ev1, ev1);\n  QBDI::rword ev2 = (v1[0] >> 32) & 0xffffffff;\n  ev2 |= (ev2 << 32);\n  checkFullFPR(fstate, 1, ev2, ev2);\n  QBDI::rword ev3 = v1[1] & 0xffffffff;\n  ev3 |= (ev3 << 32);\n  checkFullFPR(fstate, 2, ev3, ev3);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld3rv2d\") {\n\n  const char source[] = \"ld3r  { v0.2d, v1.2d, v2.2d }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0x0a18ce5402b84b8aull,\n                      0x78385dce3a634b5eull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD3Rv2d\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkFullFPR(fstate, 0, v1[0], v1[0]);\n  checkFullFPR(fstate, 1, v1[1], v1[1]);\n  checkFullFPR(fstate, 2, v1[2], v1[2]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Onev8b\") {\n\n  const char source[] = \"ld1  { v0.8b }, [x0]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Onev8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Twov8b\") {\n\n  const char source[] = \"ld1  { v0.8b, v1.8b }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Twov8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1[0]);\n  checkLowFPR(fstate, 1, v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld2Twov8b\") {\n\n  const char source[] = \"ld2  { v0.8b, v1.8b }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD2Twov8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev[2] = {0ull, 0ull};\n  for (int j = 0; j < 8 * 2; j++) {\n    ev[j % 2] |= ((v1[j / 8] >> ((j % 8) * 8)) & 0xff) << ((j / 2) * 8);\n  }\n  checkLowFPR(fstate, 0, ev[0]);\n  checkLowFPR(fstate, 1, ev[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Threev8b\") {\n\n  const char source[] = \"ld1  { v0.8b, v1.8b, v2.8b }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                      0x9265fd6758aa9205ull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Threev8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1[0]);\n  checkLowFPR(fstate, 1, v1[1]);\n  checkLowFPR(fstate, 2, v1[2]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld3Threev8b\") {\n\n  const char source[] = \"ld3  { v0.8b, v1.8b, v2.8b }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                      0x9265fd6758aa9205ull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD3Threev8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev[3] = {0ull, 0ull, 0ull};\n  for (int j = 0; j < 8 * 3; j++) {\n    ev[j % 3] |= ((v1[j / 8] >> ((j % 8) * 8)) & 0xff) << ((j / 3) * 8);\n  }\n  checkLowFPR(fstate, 0, ev[0]);\n  checkLowFPR(fstate, 1, ev[1]);\n  checkLowFPR(fstate, 2, ev[2]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Fourv8b\") {\n\n  const char source[] = \"ld1  { v0.8b, v1.8b, v2.8b, v3.8b }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                      0x9265fd6758aa9205ull, 0xb9986f96bec2bbd6ull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Fourv8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  setFPR(fstate, 3, 0xd8a0052563bc4ccaull, 0x1717581f6a684b0full);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1[0]);\n  checkLowFPR(fstate, 1, v1[1]);\n  checkLowFPR(fstate, 2, v1[2]);\n  checkLowFPR(fstate, 3, v1[3]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld4Fourv8b\") {\n\n  const char source[] = \"ld4  { v0.8b, v1.8b, v2.8b, v3.8b }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                      0x9265fd6758aa9205ull, 0xb9986f96bec2bbd6ull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD4Fourv8b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  setFPR(fstate, 3, 0xd8a0052563bc4ccaull, 0x1717581f6a684b0full);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  QBDI::rword ev[4] = {0ull, 0ull, 0ull, 0ull};\n  for (int j = 0; j < 8 * 4; j++) {\n    ev[j % 4] |= ((v1[j / 8] >> ((j % 8) * 8)) & 0xff) << ((j / 4) * 8);\n  }\n  checkLowFPR(fstate, 0, ev[0]);\n  checkLowFPR(fstate, 1, ev[1]);\n  checkLowFPR(fstate, 2, ev[2]);\n  checkLowFPR(fstate, 3, ev[3]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Fourv16b\") {\n\n  const char source[] = \"ld1  { v0.16b, v1.16b, v2.16b, v3.16b }, [x0]\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                      0x9265fd6758aa9205ull, 0xb9986f96bec2bbd6ull,\n                      0xf1410141b7604984ull, 0xfede641155b54c9dull,\n                      0x0a91424a2ff449b4ull, 0x6c9e77ad310f47abull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Fourv16b\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  setFPR(fstate, 3, 0xd8a0052563bc4ccaull, 0x1717581f6a684b0full);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkFullFPR(fstate, 0, v1[1], v1[0]);\n  checkFullFPR(fstate, 1, v1[3], v1[2]);\n  checkFullFPR(fstate, 2, v1[5], v1[4]);\n  checkFullFPR(fstate, 3, v1[7], v1[6]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Onev8b_POST\") {\n\n  const char source[] = \"ld1  { v0.8b }, [x0], #8\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Onev8b_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Fourv8b_POST\") {\n\n  const char source[] = \"ld1  { v0.8b, v1.8b, v2.8b, v3.8b }, [x0], #32\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                      0x9265fd6758aa9205ull, 0xb9986f96bec2bbd6ull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Fourv8b_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  setFPR(fstate, 3, 0xd8a0052563bc4ccaull, 0x1717581f6a684b0full);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1[0]);\n  checkLowFPR(fstate, 1, v1[1]);\n  checkLowFPR(fstate, 2, v1[2]);\n  checkLowFPR(fstate, 3, v1[3]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1Fourv1d_POST\") {\n\n  const char source[] = \"ld1  { v0.1d, v1.1d, v2.1d, v3.1d }, [x0], #32\\n\";\n\n  QBDI::rword v1[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                      0x9265fd6758aa9205ull, 0xb9986f96bec2bbd6ull};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1Fourv1d_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  setFPR(fstate, 3, 0xd8a0052563bc4ccaull, 0x1717581f6a684b0full);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkLowFPR(fstate, 0, v1[0]);\n  checkLowFPR(fstate, 1, v1[1]);\n  checkLowFPR(fstate, 2, v1[2]);\n  checkLowFPR(fstate, 3, v1[3]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1i64_POST\") {\n\n  const char source[] = \"ld1  { v0.d }[0], [x0], #8\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1i64_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkFullFPR(fstate, 0, 0x946fcddeb64b95d0ull, v1);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1i64_POST_2\") {\n\n  const char source[] = \"ld1  { v0.d }[1], [x0], #8\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1i64_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkFullFPR(fstate, 0, v1, 0x426ca4a93f5b418bull);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld1i8_POST\") {\n\n  const char source[] = \"ld1  { v0.b }[3], [x0], #1\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD1i8_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkFullFPR(fstate, 0, 0x946fcddeb64b95d0ull,\n               0x426ca4a9005b418bull | ((v1 & 0xff) << (3 * 8)));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ld4i8_POST\") {\n\n  const char source[] = \"ld4  { v0.b, v1.b, v2.b, v3.b }[3], [x0], #4\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xffffffff, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD4i8_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, 0x426ca4a93f5b418bull);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, 0xdf8f31ec3dd54a56ull);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, 0x0f50a5e4aaee4ed0ull);\n  setFPR(fstate, 3, 0xd8a0052563bc4ccaull, 0x1717581f6a684b0full);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  checkFullFPR(fstate, 0, 0x946fcddeb64b95d0ull,\n               0x426ca4a9005b418bull | ((v1 & 0xff) << (3 * 8)));\n  checkFullFPR(fstate, 1, 0xac0b75912b4f4f06ull,\n               0xdf8f31ec00d54a56ull | (((v1 >> 8) & 0xff) << (3 * 8)));\n  checkFullFPR(fstate, 2, 0x9e635973ed71406cull,\n               0x0f50a5e400ee4ed0ull | (((v1 >> 16) & 0xff) << (3 * 8)));\n  checkFullFPR(fstate, 3, 0xd8a0052563bc4ccaull,\n               0x1717581f00684b0full | (((v1 >> 24) & 0xff) << (3 * 8)));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-st1Threev8b\") {\n\n  const char source[] = \"st1  { v0.8b, v1.8b, v2.8b }, [x0]\\n\";\n\n  QBDI::rword v[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                     0x9265fd6758aa9205ull};\n  QBDI::rword dest[] = {0ull, 0ull, 0ull};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&dest[0], v[0], 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&dest[1], v[1], 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&dest[2], v[2], 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"ST1Threev8b\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&dest);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, v[0]);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, v[1]);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, v[2]);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v[0] == dest[0]);\n  CHECK(v[1] == dest[1]);\n  CHECK(v[2] == dest[2]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-st1Threev8b_POST\") {\n\n  const char source[] = \"st1  { v0.8b, v1.8b, v2.8b }, [x0], #24\\n\";\n\n  QBDI::rword v[] = {0xab3672016bef61aeull, 0xa5949fc25fcfe5fdull,\n                     0x9265fd6758aa9205ull};\n  QBDI::rword dest[] = {0ull, 0ull, 0ull};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&dest[0], v[0], 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&dest[1], v[1], 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&dest[2], v[2], 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"ST1Threev8b_POST\", QBDI::POSTINST, checkAccess,\n                   &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = reinterpret_cast<QBDI::rword>(&dest);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  setFPR(fstate, 0, 0x946fcddeb64b95d0ull, v[0]);\n  setFPR(fstate, 1, 0xac0b75912b4f4f06ull, v[1]);\n  setFPR(fstate, 2, 0x9e635973ed71406cull, v[2]);\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v[0] == dest[0]);\n  CHECK(v[1] == dest[1]);\n  CHECK(v[2] == dest[2]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-swp\") {\n\n  if (!checkFeature(\"lse\")) {\n    return;\n  }\n\n  const char source[] = \"swp x0, x1, [x2]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  QBDI::rword v2 = 0x8e060b1505409a1bull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1, v2, 8, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SWPX\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"SWPX\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = v2;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::DEFAULT, {\"lse\"});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_AARCH64-ldaddb\") {\n\n  if (!checkFeature(\"lse\")) {\n    return;\n  }\n\n  const char source[] = \"ldaddb w0, w1, [x2]\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61aeull;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1 & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v1 & 0xff, 1, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1, ((v1 & 0xff) + 3) % 256, 1, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDADDB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"LDADDB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->x0 = 3;\n  state->x1 = 0;\n  state->x2 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::DEFAULT, {\"lse\"});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n"
  },
  {
    "path": "test/API/AARCH64/VMTest_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"VMTest_AARCH64.h\"\n\n#define MNEM_IMM_SHORT_VAL 66\n#define MNEM_IMM_VAL 42424242\n#define MNEM_IMM_SHORT_STRVAL \"66\"\n#define MNEM_IMM_STRVAL \"42424242\"\n\nconst struct TestInst TestInsts[MNEM_COUNT] = {\n    {4,\n     4,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_NONE, 0, 8, 0, -1, \"XZR\",\n          QBDI::REGISTER_WRITE},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 3, \"X3\",\n          QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, MNEM_IMM_SHORT_VAL, 8, 0,\n          -1, nullptr, QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, 0, 8, 0, -1, nullptr,\n          QBDI::REGISTER_UNUSED},\n     }}};\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0) {\n  QBDI::rword volatile res = arg0 + 0x666;\n  asm(\"cmp x3, #\" MNEM_IMM_SHORT_STRVAL);\n  return res;\n}\n\n// clang-format off\nstd::vector<uint8_t> VMTest_AARCH64_InvalidInstruction = {\n  0x80, 0x0c, 0x80, 0xd2,     // movz     x0, #0x64\n  0x21, 0x00, 0x01, 0xca,     // eor      x1, x1, x1\n  0x21, 0x00, 0x00, 0x8b,     // add      x1, x1, x0\n  0x00, 0x04, 0x00, 0xd1,     // sub      x0, x0, #1\n  0x1f, 0x00, 0x00, 0xf1,     // cmp      x0, #0\n  0xff, 0xff, 0xff, 0xff,     // invalid instruction\n  0xaa, 0xab                  // unaligned instruction\n};\n\nstd::vector<uint8_t> VMTest_AARCH64_BreakingInstruction = {\n  0x80, 0x0c, 0x80, 0xd2,     // movz     x0, #0x64\n  0x21, 0x00, 0x01, 0xca,     // eor      x1, x1, x1\n  0x21, 0x00, 0x00, 0x8b,     // add      x1, x1, x0\n  0x00, 0x04, 0x00, 0xd1,     // sub      x0, x0, #1\n  0x1f, 0x00, 0x00, 0xf1,     // cmp      x0, #0\n  0xc0, 0x03, 0x5f, 0xd6      // ret\n};\n\nstd::vector<uint8_t> VMTest_AARCH64_SelfModifyingCode1 = {\n  0xe0, 0xcb, 0x9a, 0xd2,     // movz x0, #0xd65f\n  0x01, 0x78, 0x80, 0xd2,     // movz x1, #0x3c0\n  0x21, 0x40, 0x00, 0x8b,     // add\tx1, x1, x0, lsl #16\n  0x40, 0x05, 0x80, 0xd2,     // mov\tx0, #0x2a\n  0x02, 0x00, 0x00, 0x10,     // adr  x2, #0x0\n  0x41, 0x80, 0x00, 0xb8,     // stur w1, [x2, #8]\n  0xff, 0xff, 0xff, 0xff      // invalid instruction, replaced by 'ret'\n};\n\nstd::vector<uint8_t> VMTest_AARCH64_SelfModifyingCode2 = {\n  0xe0, 0xcb, 0x9a, 0xd2,     // movz x0, #0xd65f\n  0x01, 0x78, 0x80, 0xd2,     // movz x1, #0x3c0\n  0x21, 0x40, 0x00, 0x8b,     // add\tx1, x1, x0, lsl #16\n  0x40, 0x05, 0x80, 0xd2,     // mov\tx0, #0x2a\n  0x02, 0x00, 0x00, 0x10,     // adr  x2, #0x0\n  0x41, 0x80, 0x00, 0xb8,     // stur w1, [x2, #8]\n  0xe0, 0x03, 0x1f, 0xd6      // br xzr  replaced by 'ret'\n};\n// clang-format on\n\nstd::unordered_map<std::string, SizedTestCode> TestCode = {\n    {\"VMTest-InvalidInstruction\", {VMTest_AARCH64_InvalidInstruction, 0x10}},\n    {\"VMTest-BreakingInstruction\", {VMTest_AARCH64_BreakingInstruction, 0x10}},\n    {\"VMTest-SelfModifyingCode1\", {VMTest_AARCH64_SelfModifyingCode1}},\n    {\"VMTest-SelfModifyingCode2\", {VMTest_AARCH64_SelfModifyingCode2}}};\n"
  },
  {
    "path": "test/API/AARCH64/VMTest_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDITEST_VMTEST_AARCH64_H\n#define QBDITEST_VMTEST_AARCH64_H\n\n#include <unordered_map>\n#include <vector>\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/VM.h\"\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0);\n\n#define MNEM_COUNT 1u\n#if defined(QBDI_PTRAUTH)\n#define MNEM_VALIDATION 32u\n#else\n#define MNEM_VALIDATION 33u\n#endif\n#define MAX_OPERAND 4\n#define MNEM_CMP \"SUBS*\"\n\nstruct SizedTestCode {\n  std::vector<uint8_t> code;\n  std::size_t size;\n};\n\nstruct TestInst {\n  uint32_t instSize;\n  uint8_t numOperands;\n  bool isCompare;\n  QBDI::RegisterAccessType flagsAccess;\n  QBDI::OperandAnalysis operands[MAX_OPERAND];\n};\n\nextern const struct TestInst TestInsts[MNEM_COUNT];\nextern std::unordered_map<std::string, SizedTestCode> TestCode;\n\n#define SKIPTESTASM \"nop\\nnop\\nret\\n\"\n\n#endif\n"
  },
  {
    "path": "test/API/AARCH64/VMTest_AARCH64_LDRL.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cstdint>\n#include <stdio.h>\n\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n#include \"QBDI/Memory.hpp\"\n\nQBDI_NOINLINE uint64_t ldrlX() {\n  uint64_t r;\n\n  asm(\"  ldr %0, labelX\\n\"\n      \"  b targetX\\n\"\n      \"labelX:\\n\"\n      \"  brk 65535\\n\"\n      \"  brk 65535\\n\"\n      \"targetX:\\n\"\n      \"  nop\\n\"\n      : \"=r\"(r));\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LDRLX\") {\n  QBDI::rword retval = 0;\n\n  vm.call(&retval, (QBDI::rword)ldrlX, {});\n  CHECK(retval == 0xD43FFFE0D43FFFE0ull);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t ldrlW_1() {\n  uint64_t r;\n\n  asm(\"  ldr w1, labelW\\n\"\n      \"  mov %0, x1\\n\"\n      \"  b targetW\\n\"\n      \"labelW:\\n\"\n      \"  brk 65535\\n\"\n      \"targetW:\\n\"\n      \"  nop\\n\"\n      : \"=r\"(r)::\"x1\");\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LDRLW_1\") {\n  QBDI::rword retval = 0;\n\n  vm.call(&retval, (QBDI::rword)ldrlW_1, {});\n  CHECK(retval == 0xD43FFFE0ull);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t ldrlW_2() {\n  uint64_t r;\n\n  asm(\"  b targetW2\\n\"\n      \"labelW2:\\n\"\n      \"  brk 65535\\n\"\n      \"targetW2:\\n\"\n      \"  ldr w1, labelW2\\n\"\n      \"  mov %0, x1\\n\"\n      : \"=r\"(r)::\"x1\");\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LDRLW_2\") {\n  QBDI::rword retval = 0;\n\n  vm.call(&retval, (QBDI::rword)ldrlW_2, {});\n  CHECK(retval == 0xD43FFFE0ull);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t ldrlSW_1() {\n  uint64_t r;\n\n  asm(\"  ldrsw %0, labelSW\\n\"\n      \"  b targetSW\\n\"\n      \"labelSW:\\n\"\n      \"  brk 65535\\n\"\n      \"targetSW:\\n\"\n      \"  nop\\n\"\n      : \"=r\"(r));\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LDRLSW_1\") {\n  QBDI::rword retval = 0;\n\n  vm.call(&retval, (QBDI::rword)ldrlSW_1, {});\n  CHECK(retval == 0xFFFFFFFFD43FFFE0ull);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t ldrlSW_2() {\n  uint64_t r;\n\n  asm(\"  b targetSW2\\n\"\n      \"labelSW2:\\n\"\n      \"  brk 65535\\n\"\n      \"targetSW2:\\n\"\n      \"  ldrsw %0, labelSW2\\n\"\n      : \"=r\"(r));\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LDRLSW_2\") {\n  QBDI::rword retval = 0;\n\n  vm.call(&retval, (QBDI::rword)ldrlSW_2, {});\n  CHECK(retval == 0xFFFFFFFFD43FFFE0ull);\n\n  SUCCEED();\n}\n"
  },
  {
    "path": "test/API/AARCH64/VMTest_AARCH64_LocalMonitor.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cstdint>\n#include <stdio.h>\n\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n#include \"QBDI/Memory.hpp\"\n\nnamespace {\nQBDI::VMAction x28CBK(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                      QBDI::FPRState *fprState, void *data) {\n  REQUIRE(gprState->x28 == 0);\n  return QBDI::VMAction::CONTINUE;\n}\n} // namespace\n\nQBDI_NOINLINE uint64_t simpleStore() {\n  uint64_t v = 0x20;\n\n  asm(\"ldxr x1, [%0]\\n\"\n      \"add   x1, x1, #1\\n\"\n      \"stxr  w0, x1, [%0]\\n\"\n      :\n      : \"r\"(&v)\n      : \"x0\", \"x1\", \"memory\");\n\n  return v;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LocalMonitor-simpleStore\") {\n  QBDI::rword retval;\n\n  vm.addCodeCB(QBDI::InstPosition::PREINST, x28CBK, nullptr);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, x28CBK, nullptr);\n  vm.call(&retval, (QBDI::rword)simpleStore, {});\n  REQUIRE(retval == (QBDI::rword)0x21);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t clearMonitor() {\n  uint64_t v = 0x20;\n\n  asm(\"ldxr x1, [%0]\\n\"\n      \"add   x1, x1, #1\\n\"\n      \"clrex\\n\"\n      \"stxr  w0, x1, [%0]\\n\"\n      :\n      : \"r\"(&v)\n      : \"x0\", \"x1\", \"memory\");\n\n  return v;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LocalMonitor-clearMonitor\") {\n  QBDI::rword retval;\n\n  vm.addCodeCB(QBDI::InstPosition::PREINST, x28CBK, nullptr);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, x28CBK, nullptr);\n  vm.call(&retval, (QBDI::rword)clearMonitor, {});\n  REQUIRE(retval == (QBDI::rword)0x20);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t doubleStackStore() {\n  uint64_t v[2] = {0x20, 0x30};\n\n  asm(\"ldxr x1, [%0]\\n\"\n      \"add   x1, x1, #1\\n\"\n\n      \"ldxr x2, [%1]\\n\"\n      \"add   x2, x2, #1\\n\"\n      \"stxr  w0, x2, [%1]\\n\"\n\n      \"stxr  w0, x1, [%0]\\n\"\n      :\n      : \"r\"(&v[0]), \"r\"(&v[1])\n      : \"x0\", \"x1\", \"x2\", \"memory\");\n\n  return v[0] | (v[1] << 16);\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LocalMonitor-doubleStackStore\") {\n  QBDI::rword retval;\n\n  vm.addCodeCB(QBDI::InstPosition::PREINST, x28CBK, nullptr);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, x28CBK, nullptr);\n  vm.call(&retval, (QBDI::rword)doubleStackStore, {});\n  REQUIRE(retval == (QBDI::rword)0x310020);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t doubleStore(uint64_t *arg) {\n  uint64_t v = 0x20;\n\n  asm(\"ldxr x1, [%0]\\n\"\n      \"add   x1, x1, #1\\n\"\n\n      \"ldxr x2, [%1]\\n\"\n      \"stxr  w0, x1, [%0]\\n\"\n      \"add   x2, x2, #1\\n\"\n      \"stxr  w0, x2, [%1]\\n\"\n\n      :\n      : \"r\"(&v), \"r\"(arg)\n      : \"x0\", \"x1\", \"x2\", \"memory\");\n\n  return v;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_LocalMonitor-doubleStore\") {\n  QBDI::rword retval;\n  // stack and heap isn't in the same exclusive range\n  uint64_t *allocated_val = new uint64_t;\n  *allocated_val = 0x30;\n\n  vm.addCodeCB(QBDI::InstPosition::PREINST, x28CBK, nullptr);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, x28CBK, nullptr);\n  vm.call(&retval, (QBDI::rword)doubleStore, {(QBDI::rword)allocated_val});\n  REQUIRE(retval == (QBDI::rword)0x20);\n  REQUIRE(*allocated_val == 0x30);\n\n  delete allocated_val;\n  SUCCEED();\n}\n"
  },
  {
    "path": "test/API/AARCH64/VMTest_AARCH64_X28.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cstdint>\n#include <stdio.h>\n\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n#include \"QBDI/Memory.hpp\"\n\nstatic QBDI::VMAction dummyCB(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                              QBDI::FPRState *fprState, void *data) {\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI_NOINLINE uint64_t useX28_simple() {\n  uint64_t v = 0x20;\n  uint64_t r = 0x0;\n\n  asm(\"  mov  x28, %1\\n\"\n      \"  mov %0, x28\\n\"\n      : \"=r\"(r)\n      : \"r\"(v)\n      : \"x28\");\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_X28_RegisterSet_simple\") {\n  QBDI::rword retval = 0;\n\n  state->x28 = 0;\n  vm.call(&retval, (QBDI::rword)useX28_simple, {});\n  INFO(\"Case x28 == 0\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  state->x28 = 0x3269;\n  retval = 0;\n  vm.call(&retval, (QBDI::rword)useX28_simple, {});\n  INFO(\"Case x28 == 0x3269\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  state->x28 = 0x0;\n  retval = 0;\n  uint32_t instrId =\n      vm.addCodeCB(QBDI::InstPosition::PREINST, dummyCB, nullptr);\n  vm.call(&retval, (QBDI::rword)useX28_simple, {});\n  INFO(\"Case With PreInst Callback\");\n  CHECK(retval == (QBDI::rword)0x20);\n  vm.deleteInstrumentation(instrId);\n\n  state->x28 = 0x0;\n  retval = 0;\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, dummyCB, nullptr);\n  vm.call(&retval, (QBDI::rword)useX28_simple, {});\n  INFO(\"Case With PostInst Callback\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  state->x28 = 0x0;\n  retval = 0;\n  vm.addCodeCB(QBDI::InstPosition::PREINST, dummyCB, nullptr);\n  vm.call(&retval, (QBDI::rword)useX28_simple, {});\n  INFO(\"Case With PreInst and PostInst Callback\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  SUCCEED();\n}\n\nQBDI_NOINLINE uint64_t useX28_crossBB() {\n  uint64_t v = 0x20;\n  uint64_t r = 0x0;\n\n  asm(\"  mov  x28, %1\\n\"\n      \"  b label\\n\"\n      \"label:\\n\"\n      \"  mov %0, x28\\n\"\n      : \"=r\"(r)\n      : \"r\"(v)\n      : \"x28\");\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest_AARCH64_X28_RegisterSet_crossBB\") {\n  QBDI::rword retval = 0;\n\n  state->x28 = 0;\n  vm.call(&retval, (QBDI::rword)useX28_crossBB, {});\n  INFO(\"Case x28 == 0\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  state->x28 = 0x3269;\n  retval = 0;\n  vm.call(&retval, (QBDI::rword)useX28_crossBB, {});\n  INFO(\"Case x28 == 0x3269\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  state->x28 = 0x0;\n  retval = 0;\n  uint32_t instrId =\n      vm.addCodeCB(QBDI::InstPosition::PREINST, dummyCB, nullptr);\n  vm.call(&retval, (QBDI::rword)useX28_crossBB, {});\n  INFO(\"Case With PreInst Callback\");\n  CHECK(retval == (QBDI::rword)0x20);\n  vm.deleteInstrumentation(instrId);\n\n  state->x28 = 0x0;\n  retval = 0;\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, dummyCB, nullptr);\n  vm.call(&retval, (QBDI::rword)useX28_crossBB, {});\n  INFO(\"Case With PostInst Callback\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  state->x28 = 0x0;\n  retval = 0;\n  vm.addCodeCB(QBDI::InstPosition::PREINST, dummyCB, nullptr);\n  vm.call(&retval, (QBDI::rword)useX28_crossBB, {});\n  INFO(\"Case With PreInst and PostInst Callback\");\n  CHECK(retval == (QBDI::rword)0x20);\n\n  SUCCEED();\n}\n"
  },
  {
    "path": "test/API/APITest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"APITest.h\"\n#include <catch2/catch_test_macros.hpp>\n\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#define STACK_SIZE 4096\n\nstatic void dummyFn() {}\n\nAPITest::APITest() : vm() {\n\n  bool instrumented = vm.addInstrumentedModuleFromAddr((QBDI::rword)&dummyFn);\n  REQUIRE(instrumented);\n\n  // get GPR state\n  state = vm.getGPRState();\n\n  // Get a pointer to the GPR state of the vm\n  // Setup initial GPR state, this fakestack will produce a ret NULL at the end\n  // of the execution\n  bool ret = QBDI::allocateVirtualStack(state, STACK_SIZE, &fakestack);\n  REQUIRE(ret == true);\n}\n\nAPITest::~APITest() {\n  QBDI::alignedFree(fakestack);\n  objects.clear();\n}\n\nQBDI::rword APITest::genASM(const char *source, QBDI::CPUMode cpuMode,\n                            const std::vector<std::string> mattrs) {\n  std::ostringstream finalSource;\n\n#ifdef QBDI_ARCH_ARM\n  if (cpuMode == QBDI::CPUMode::Thumb) {\n    finalSource << \".thumb\\n\";\n  }\n  finalSource << source << \"\\n\"\n              << \"bx lr\\n\";\n  if (cpuMode == QBDI::CPUMode::ARM) {\n    objects.emplace_back(finalSource.str().c_str(), \"\", \"arm\", mattrs);\n  } else {\n    objects.emplace_back(finalSource.str().c_str(), \"\", \"thumb\", mattrs);\n  }\n#else\n  finalSource << source << \"\\n\"\n              << \"ret\\n\";\n  if constexpr (QBDI::is_aarch64) {\n    objects.emplace_back(finalSource.str().c_str(), \"\", \"aarch64\", mattrs);\n  } else {\n    objects.emplace_back(finalSource.str().c_str(), \"\", \"\", mattrs);\n  }\n#endif\n\n  const llvm::ArrayRef<uint8_t> &code = objects.back().getCode();\n  QBDI::rword codeAddr = (QBDI::rword)code.data();\n  llvm::sys::Memory::InvalidateInstructionCache(code.data(), code.size());\n\n  vm.addInstrumentedRange(codeAddr, codeAddr + code.size());\n  vm.clearCache(codeAddr, codeAddr + code.size());\n\n#ifdef QBDI_ARCH_ARM\n  if (cpuMode == QBDI::CPUMode::Thumb) {\n    codeAddr |= 1;\n  }\n#endif\n\n  vm.precacheBasicBlock(codeAddr);\n\n  return codeAddr;\n}\n\nbool APITest::runOnASM(QBDI::rword *retval, const char *source,\n                       const std::vector<QBDI::rword> &args,\n                       QBDI::CPUMode cpuMode,\n                       const std::vector<std::string> mattrs) {\n  QBDI::rword addr = genASM(source, cpuMode, mattrs);\n\n  return vm.call(retval, addr, args);\n}\n"
  },
  {
    "path": "test/API/APITest.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef QBDITEST_APITEST_H\n#define QBDITEST_APITEST_H\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/VM.h\"\n\nclass APITest {\nprivate:\n  std::vector<InMemoryObject> objects;\n\nprotected:\n  QBDI::GPRState *state;\n  uint8_t *fakestack;\n\npublic:\n  QBDI::VM vm;\n\n  APITest();\n  ~APITest();\n\n  QBDI::rword genASM(const char *source,\n                     QBDI::CPUMode cpuMode = QBDI::CPUMode::DEFAULT,\n                     const std::vector<std::string> mattrs = {});\n\n  bool runOnASM(QBDI::rword *retval, const char *source,\n                const std::vector<QBDI::rword> &args = {},\n                QBDI::CPUMode cpuMode = QBDI::CPUMode::DEFAULT,\n                const std::vector<std::string> mattrs = {});\n};\n\n#endif /* QBDITEST_APITEST_H */\n"
  },
  {
    "path": "test/API/ARM/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysisTest_ARM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysisTest_Thumb.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest_ARM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest_ARM_LDM_STM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest_Thumb.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest_Thumb_LDM_STM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/VMTest_ARM.cpp\")\n"
  },
  {
    "path": "test/API/ARM/InstAnalysisTest_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n\nstruct ExpectedInstAnalysis {\n  std::string mnemonic;\n  QBDI::rword address;\n  QBDI::CPUMode cpuMode;\n  uint32_t instSize;\n  bool affectControlFlow;\n  bool isBranch;\n  bool isCall;\n  bool isReturn;\n  bool isCompare;\n  bool isPredicable;\n  bool mayLoad;\n  bool mayStore;\n  uint32_t loadSize;\n  uint32_t storeSize;\n  QBDI::ConditionType condition;\n};\n\n[[maybe_unused]] static void debugOperand(const QBDI::InstAnalysis *ana) {\n  if ((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n      QBDI::ANALYSIS_OPERANDS) {\n    for (int i = 0; i < ana->numOperands; i++) {\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      WARN(\"- type: \" << op.type << \", flag: \" << op.flag\n                      << \", value: \" << op.value << \", size: \" << (int)op.size\n                      << \", regOff : \" << (int)op.regOff\n                      << \", regCtxIdx: \" << op.regCtxIdx << \", regName: \"\n                      << (op.regName == nullptr ? \"nullptr\" : op.regName)\n                      << \", regAccess: \"\n                      << (op.regAccess & QBDI::REGISTER_READ ? \"r\" : \"-\")\n                      << (op.regAccess & QBDI::REGISTER_WRITE ? \"w\" : \"-\"));\n    }\n  }\n}\n\nstatic void checkOperand(const QBDI::InstAnalysis *ana,\n                         const std::vector<QBDI::OperandAnalysis> expecteds,\n                         QBDI::RegisterAccessType flagsAccess) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n             QBDI::ANALYSIS_OPERANDS) {\n    CHECK(flagsAccess == ana->flagsAccess);\n    CHECK(expecteds.size() == ana->numOperands);\n    for (unsigned i = 0;\n         i < std::min<unsigned>(ana->numOperands, expecteds.size()); i++) {\n      const QBDI::OperandAnalysis &expect = expecteds[i];\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      INFO(\"For operand \" << i);\n\n      CHECK(expect.type == op.type);\n      CHECK(expect.flag == op.flag);\n      if (op.type == QBDI::OPERAND_IMM || expect.value != 0) {\n        CHECK(expect.value == op.value);\n      }\n      CHECK(expect.size == op.size);\n      CHECK(expect.regOff == op.regOff);\n      CHECK(expect.regCtxIdx == op.regCtxIdx);\n      CHECK(expect.regAccess == op.regAccess);\n\n      const std::string expectedRegName(\n          (expect.regName != nullptr) ? expect.regName : \"\");\n      const std::string foundRegName((op.regName != nullptr) ? op.regName : \"\");\n      CHECK(expectedRegName == foundRegName);\n\n      if (expect.regName == nullptr || op.regName == nullptr) {\n        CHECK(expect.regName == op.regName);\n      }\n    }\n  }\n}\n\nstatic void checkInst(const QBDI::InstAnalysis *ana,\n                      const ExpectedInstAnalysis expected) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_INSTRUCTION) ==\n             QBDI::ANALYSIS_INSTRUCTION) {\n    CHECK(expected.mnemonic == ana->mnemonic);\n    CHECK(expected.address == ana->address);\n    CHECK(expected.instSize == ana->instSize);\n    CHECK(expected.cpuMode == ana->cpuMode);\n    CHECK(expected.affectControlFlow == ana->affectControlFlow);\n    CHECK(expected.isBranch == ana->isBranch);\n    CHECK(expected.isCall == ana->isCall);\n    CHECK(expected.isReturn == ana->isReturn);\n    CHECK(expected.isCompare == ana->isCompare);\n    CHECK(expected.isPredicable == ana->isPredicable);\n    CHECK(expected.mayLoad == ana->mayLoad);\n    CHECK(expected.mayStore == ana->mayStore);\n    CHECK(expected.loadSize == ana->loadSize);\n    CHECK(expected.storeSize == ana->storeSize);\n    CHECK(expected.condition == ana->condition);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-CachedInst\") {\n\n  QBDI::rword addr = genASM(\"bic r0, r0, 0xff\\n\");\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n\n  vm.clearAllCache();\n\n  CHECK(vm.getCachedInstAnalysis(addr) == nullptr);\n\n  vm.precacheBasicBlock(addr);\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-bx_lr\") {\n\n  QBDI::rword addr = genASM(\"bx lr\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"BX_RET\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ true, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 14,\n                    \"LR\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-beq\") {\n\n  QBDI::rword addr = genASM(\"beq label\\nnop\\nnop\\nnop\\nlabel: nop\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"Bcc\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 8, 2, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-add\") {\n\n  QBDI::rword addr = genASM(\"add r1, r2, r1\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADDrr\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-addsne\") {\n\n  QBDI::rword addr = genASM(\"addsne r1, r2, r1\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADDrr\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NOT_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_READ_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-pop1\") {\n\n  QBDI::rword addr = genASM(\"pop {r0-r4}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDMIA_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 20, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"R0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 1, \"R1\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 2, \"R2\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 3, \"R3\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"R4\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-pop2\") {\n\n  QBDI::rword addr = genASM(\"pop {r0-r4,r8-r12}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDMIA_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 40, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"R0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 1, \"R1\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 2, \"R2\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 3, \"R3\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"R4\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 8, \"R8\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 9, \"R9\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 10, \"R10\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 11, \"R11\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 12, \"R12\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-pop3\") {\n\n  QBDI::rword addr = genASM(\"pop {r0-r12,lr,pc}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDMIA_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 60, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"R0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 1, \"R1\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 2, \"R2\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 3, \"R3\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"R4\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 5, \"R5\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 6, \"R6\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 7, \"R7\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 8, \"R8\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 9, \"R9\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 10, \"R10\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 11, \"R11\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 12, \"R12\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 14, \"LR\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 15, \"PC\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-push1\") {\n\n  QBDI::rword addr = genASM(\"push {r0-r4}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"STMDB_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 20,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"R0\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 1, \"R1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 2, \"R2\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 3, \"R3\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"R4\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-push2\") {\n\n  QBDI::rword addr = genASM(\"push {r0-r4,r8-r12}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"STMDB_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 40,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"R0\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 1, \"R1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 2, \"R2\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 3, \"R3\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"R4\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 8, \"R8\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 9, \"R9\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 10, \"R10\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 11, \"R11\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 12, \"R12\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-push3\") {\n\n  QBDI::rword addr = genASM(\"push {r0-r12,lr,pc}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"STMDB_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 60,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"R0\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 1, \"R1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 2, \"R2\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 3, \"R3\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"R4\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 5, \"R5\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 6, \"R6\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 7, \"R7\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 8, \"R8\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 9, \"R9\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 10, \"R10\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 11, \"R11\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 12, \"R12\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 14, \"LR\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 15, \"PC\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-sadd8\") {\n\n  QBDI::rword addr = genASM(\"sadd8 r0, r1, r2\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"SADD8\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-mrs\") {\n\n  QBDI::rword addr = genASM(\"mrs r0, APSR\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MRS\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-msr\") {\n\n  QBDI::rword addr = genASM(\"msr APSR_nzcvqg, r0\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MSR\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 12,\n                    4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-msri\") {\n\n  QBDI::rword addr = genASM(\"msr APSR_nzcvqg, #0\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MSRi\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 12,\n                    4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-vldm1\") {\n\n  QBDI::rword addr = genASM(\"vldmia\tr0, {s0, s1, s2, s3}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VLDMSIA\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 16, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"S0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"S1\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 8, \"S2\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 12, \"S3\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-vldm2\") {\n\n  QBDI::rword addr = genASM(\"vldmia\tr0, {d0, d1, d2, d3}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VLDMDIA\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 32, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 0, \"D0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 8, \"D1\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 16, \"D2\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 24, \"D3\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-vldm3\") {\n\n  QBDI::rword addr = genASM(\"vldmia\tr0!, {d0, d1, d2, d3}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VLDMDIA_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 32, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 0, \"D0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 8, \"D1\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 16, \"D2\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 24, \"D3\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-vstm1\") {\n\n  QBDI::rword addr = genASM(\"vstmia\tr0, {s0, s1, s2, s3}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VSTMSIA\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 16,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 0, \"S0\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 4, \"S1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 8, \"S2\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 4,\n                    0, 12, \"S3\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-vstm2\") {\n\n  QBDI::rword addr = genASM(\"vstmia\tr0, {d0, d1, d2, d3}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VSTMDIA\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 32,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 0, \"D0\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 8, \"D1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 16, \"D2\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 24, \"D3\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-vstm3\") {\n\n  QBDI::rword addr = genASM(\"vstmia\tr0!, {d0, d1, d2, d3}\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VSTMDIA_UPD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 32,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 0, \"D0\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 8, \"D1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 16, \"D2\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 24, \"D3\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-ldrbt_imm\") {\n\n  QBDI::rword addr = genASM(\"ldrbt r0, [r10], #-0x40\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDRBT_POST_IMM\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, -0x40, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-ldrbt_reg1\") {\n\n  QBDI::rword addr = genASM(\"ldrbt r0, [r10], -r9, ror #3\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDRBT_POST_REG\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n      {\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n           QBDI::REGISTER_WRITE},\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 10, \"R10\",\n           QBDI::REGISTER_READ_WRITE},\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 9, \"R9\",\n           QBDI::REGISTER_READ},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR,\n           (/* IndexModePost */ 2 << 16) | (/* ShiftOpc::ror */ 4 << 13) |\n               (/* sub */ 1 << 12) | (/* shift */ 3),\n           4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n      },\n      QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-ldrbt_reg2\") {\n\n  QBDI::rword addr = genASM(\"ldrbt r0, [r10], r9, ror #3\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDRBT_POST_REG\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n      {\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n           QBDI::REGISTER_WRITE},\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 10, \"R10\",\n           QBDI::REGISTER_READ_WRITE},\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 9, \"R9\",\n           QBDI::REGISTER_READ},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR,\n           (/* IndexModePost */ 2 << 16) | (/* ShiftOpc::ror */ 4 << 13) |\n               (/* sub */ 0 << 12) | (/* shift */ 3),\n           4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n      },\n      QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-ldrex\") {\n\n  QBDI::rword addr = genASM(\"ldrex r0, [r10]\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDREX\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-ldrexb\") {\n\n  QBDI::rword addr = genASM(\"ldrexb r0, [r10]\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDREXB\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-ldrexd\") {\n\n  QBDI::rword addr = genASM(\"ldrexd r0, r1, [r10]\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LDREXD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-strex\") {\n\n  QBDI::rword addr = genASM(\"strex r5, r0, [r10]\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"STREX\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-strexb\") {\n\n  QBDI::rword addr = genASM(\"strexb r5, r0, [r10]\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"STREXB\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 1,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-strexd\") {\n\n  QBDI::rword addr = genASM(\"strexd r5, r0, r1, [r10]\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"STREXD\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 8,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-cmp\") {\n\n  QBDI::rword addr = genASM(\"cmp r5, #16\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMPri\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 16,\n                    4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_ARM-tst\") {\n\n  QBDI::rword addr = genASM(\"tst r5, #16\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"TSTri\", addr, QBDI::CPUMode::ARM,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 16,\n                    4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n"
  },
  {
    "path": "test/API/ARM/InstAnalysisTest_Thumb.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n\nstruct ExpectedInstAnalysis {\n  std::string mnemonic;\n  QBDI::rword address;\n  QBDI::CPUMode cpuMode;\n  uint32_t instSize;\n  bool affectControlFlow;\n  bool isBranch;\n  bool isCall;\n  bool isReturn;\n  bool isCompare;\n  bool isPredicable;\n  bool mayLoad;\n  bool mayStore;\n  uint32_t loadSize;\n  uint32_t storeSize;\n  QBDI::ConditionType condition;\n};\n\n[[maybe_unused]] static void debugOperand(const QBDI::InstAnalysis *ana) {\n  if ((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n      QBDI::ANALYSIS_OPERANDS) {\n    for (int i = 0; i < ana->numOperands; i++) {\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      WARN(\"- type: \" << op.type << \", flag: \" << op.flag\n                      << \", value: \" << op.value << \", size: \" << (int)op.size\n                      << \", regOff : \" << (int)op.regOff\n                      << \", regCtxIdx: \" << op.regCtxIdx << \", regName: \"\n                      << (op.regName == nullptr ? \"nullptr\" : op.regName)\n                      << \", regAccess: \"\n                      << (op.regAccess & QBDI::REGISTER_READ ? \"r\" : \"-\")\n                      << (op.regAccess & QBDI::REGISTER_WRITE ? \"w\" : \"-\"));\n    }\n  }\n}\n\nstatic void checkOperand(const QBDI::InstAnalysis *ana,\n                         const std::vector<QBDI::OperandAnalysis> expecteds,\n                         QBDI::RegisterAccessType flagsAccess) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n             QBDI::ANALYSIS_OPERANDS) {\n    CHECK(flagsAccess == ana->flagsAccess);\n    CHECK(expecteds.size() == ana->numOperands);\n    for (unsigned i = 0;\n         i < std::min<unsigned>(ana->numOperands, expecteds.size()); i++) {\n      const QBDI::OperandAnalysis &expect = expecteds[i];\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      INFO(\"For operand \" << i);\n\n      CHECK(expect.type == op.type);\n      CHECK(expect.flag == op.flag);\n      if (op.type == QBDI::OPERAND_IMM || expect.value != 0) {\n        CHECK(expect.value == op.value);\n      }\n      CHECK(expect.size == op.size);\n      CHECK(expect.regOff == op.regOff);\n      CHECK(expect.regCtxIdx == op.regCtxIdx);\n      CHECK(expect.regAccess == op.regAccess);\n\n      const std::string expectedRegName(\n          (expect.regName != nullptr) ? expect.regName : \"\");\n      const std::string foundRegName((op.regName != nullptr) ? op.regName : \"\");\n      CHECK(expectedRegName == foundRegName);\n\n      if (expect.regName == nullptr || op.regName == nullptr) {\n        CHECK(expect.regName == op.regName);\n      }\n    }\n  }\n}\n\nstatic void checkInst(const QBDI::InstAnalysis *ana,\n                      const ExpectedInstAnalysis expected) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_INSTRUCTION) ==\n             QBDI::ANALYSIS_INSTRUCTION) {\n    CHECK(expected.mnemonic == ana->mnemonic);\n    CHECK(expected.address == ana->address);\n    CHECK(expected.instSize == ana->instSize);\n    CHECK(expected.cpuMode == ana->cpuMode);\n    CHECK(expected.affectControlFlow == ana->affectControlFlow);\n    CHECK(expected.isBranch == ana->isBranch);\n    CHECK(expected.isCall == ana->isCall);\n    CHECK(expected.isReturn == ana->isReturn);\n    CHECK(expected.isCompare == ana->isCompare);\n    CHECK(expected.isPredicable == ana->isPredicable);\n    CHECK(expected.mayLoad == ana->mayLoad);\n    CHECK(expected.mayStore == ana->mayStore);\n    CHECK(expected.loadSize == ana->loadSize);\n    CHECK(expected.storeSize == ana->storeSize);\n    CHECK(expected.condition == ana->condition);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-CachedInst\") {\n\n  QBDI::rword addr = genASM(\"bic.W r0, r0, 0xff\\n\", QBDI::CPUMode::Thumb);\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n\n  vm.clearAllCache();\n\n  CHECK(vm.getCachedInstAnalysis(addr) == nullptr);\n\n  vm.precacheBasicBlock(addr);\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-bx_lr\") {\n\n  QBDI::rword addr = genASM(\"bx lr\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tBX\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 14,\n                    \"LR\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-bx_reg\") {\n\n  QBDI::rword addr = genASM(\"bx r0\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tBX\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-bxgt\") {\n\n  QBDI::rword addr = genASM(\"it gt\\nbxgt r0\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr + 2, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tBX\", (addr + 2) & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_GREAT});\n  checkOperand(vm.getCachedInstAnalysis(addr + 2, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-bxal\") {\n\n  QBDI::rword addr = genASM(\"it al\\nbxal r0\\n\", QBDI::CPUMode::Thumb) + 2;\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tBX\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-beq\") {\n\n  QBDI::rword addr =\n      genASM(\"beq label\\nnop\\nnop\\nnop\\nlabel: nop\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tBcc\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 4, 2, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-add\") {\n\n  QBDI::rword addr = genASM(\"add r1, r2\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tADDhirr\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-adds\") {\n\n  QBDI::rword addr = genASM(\"adds r1, r1, r2\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tADDrr\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-addal\") {\n\n  QBDI::rword addr =\n      genASM(\"it al\\naddal r1, r1, r2\\n\", QBDI::CPUMode::Thumb) + 2;\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tADDrr\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-addeq\") {\n\n  QBDI::rword addr =\n      genASM(\"it eq\\naddeq r1, r1, r2\\n\", QBDI::CPUMode::Thumb) + 2;\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tADDrr\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-ital\") {\n\n  QBDI::rword addr = genASM(\"it al\\naddal r1, r1, r2\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2IT\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n      {\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT,\n           QBDI::CONDITION_ALWAYS, 4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 8, 4, 0, -1,\n           nullptr, QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 12, 1, 0, -1,\n           \"ITSTATE\", QBDI::REGISTER_WRITE},\n      },\n      QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-iteq\") {\n\n  QBDI::rword addr = genASM(\"it eq\\naddeq r1, r1, r2\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2IT\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n      {\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT,\n           QBDI::CONDITION_EQUALS, 4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 8, 4, 0, -1,\n           nullptr, QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 12, 1, 0, -1,\n           \"ITSTATE\", QBDI::REGISTER_WRITE},\n      },\n      QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-ldrex\") {\n\n  QBDI::rword addr = genASM(\"ldrex r0, [r10, #16]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2LDREX\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 16, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-ldrexb\") {\n\n  QBDI::rword addr = genASM(\"ldrexb r0, [r10]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2LDREXB\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-ldrexd\") {\n\n  QBDI::rword addr = genASM(\"ldrexd r0, r1, [r10]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2LDREXD\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-strex\") {\n\n  QBDI::rword addr = genASM(\"strex r5, r0, [r10, #16]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2STREX\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 16, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-strexb\") {\n\n  QBDI::rword addr = genASM(\"strexb r5, r0, [r10]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2STREXB\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 1,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-strexd\") {\n\n  QBDI::rword addr = genASM(\"strexd r5, r0, r1, [r10]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2STREXD\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 8,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1, \"R1\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 10,\n                    \"R10\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-cmp\") {\n\n  QBDI::rword addr = genASM(\"cmp r5, #16\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tCMPi8\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 16,\n                    4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-tst\") {\n\n  QBDI::rword addr = genASM(\"tst r5, #16\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2TSTri\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"R5\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 16,\n                    4, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-tldrh\") {\n\n  QBDI::rword addr = genASM(\"ldrh r2, [r0, #4]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tLDRHi\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 2, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-tldr\") {\n\n  QBDI::rword addr = genASM(\"ldr r2, [r0, #4]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tLDRi\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-tstrh\") {\n\n  QBDI::rword addr = genASM(\"strh r2, [r0, #4]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tSTRHi\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 2,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-tstr\") {\n\n  QBDI::rword addr = genASM(\"str r2, [r0, #4]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tSTRi\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-tldrsp\") {\n\n  QBDI::rword addr = genASM(\"ldr r2, [sp, #4]\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tLDRspi\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-tstrsp\") {\n\n  QBDI::rword addr = genASM(\"str\tr2, [sp, #0x8]\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"tSTRspi\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 8, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-t2ldrd\") {\n\n  QBDI::rword addr =\n      genASM(\"ldrd r2, r12, [r0, #-16]!\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2LDRD_PRE\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 12,\n                    \"R12\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"R0\",\n                    QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, -16, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_Thumb-t2ldrdsp\") {\n\n  QBDI::rword addr =\n      genASM(\"ldrd r2, r12, [sp, #-16]!\\n\", QBDI::CPUMode::Thumb);\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"t2LDRD_PRE\", addr & (~1), QBDI::CPUMode::Thumb,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ true, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2, \"R2\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 12,\n                    \"R12\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 13,\n                    \"SP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, -16, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n"
  },
  {
    "path": "test/API/ARM/MemoryAccessTest_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#include \"Utility/System.h\"\n\n[[maybe_unused]] static bool checkFeature(const char *f) {\n  if (!QBDI::isHostCPUFeaturePresent(f)) {\n    WARN(\"Host doesn't support \" << f << \" feature: SKIP\");\n    return false;\n  }\n  return true;\n}\n\n[[maybe_unused]] static QBDI::VMAction debugCB(QBDI::VMInstanceRef vm,\n                                               QBDI::GPRState *gprState,\n                                               QBDI::FPRState *fprState,\n                                               void *data) {\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  printf(\"0x%x (%10s): %s\\n\", instAnalysis->address, instAnalysis->mnemonic,\n         instAnalysis->disassembly);\n\n  for (auto &a : vm->getInstMemoryAccess()) {\n    printf(\n        \" - inst: 0x%x, addr: 0x%x, size: %d, type: %c%c, \"\n        \"value: 0x%x, flags : 0x %x\\n\",\n        a.instAddress, a.accessAddress, a.size,\n        ((a.type & QBDI::MEMORY_READ) != 0) ? 'r' : '-',\n        ((a.type & QBDI::MEMORY_WRITE) != 0) ? 'w' : '-', a.value, a.flags);\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nstruct ExpectedMemoryAccess {\n  QBDI::rword address;\n  QBDI::rword value;\n  uint16_t size;\n  QBDI::MemoryAccessType type;\n  QBDI::MemoryAccessFlags flags;\n  bool see = false;\n};\n\nstruct ExpectedMemoryAccesses {\n  std::vector<ExpectedMemoryAccess> accesses;\n};\n\nstatic QBDI::VMAction checkAccess(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n\n  ExpectedMemoryAccesses *info = static_cast<ExpectedMemoryAccesses *>(data);\n  if (std::all_of(info->accesses.begin(), info->accesses.end(),\n                  [](ExpectedMemoryAccess &a) { return a.see; }))\n    return QBDI::VMAction::CONTINUE;\n\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n\n  CHECKED_IF(memaccesses.size() == info->accesses.size()) {\n    for (size_t i = 0; i < info->accesses.size(); i++) {\n      auto &memaccess = memaccesses[i];\n      auto &expect = info->accesses[i];\n      INFO(\"Expected Access n°\" << i);\n      INFO(\"Value 0x\" << std::hex << memaccess.value << \" expect 0x\" << std::hex\n                      << expect.value);\n      CHECKED_IF(memaccess.accessAddress == expect.address)\n      CHECKED_IF((memaccess.value == expect.value || expect.value == 0))\n      CHECKED_IF(memaccess.size == expect.size)\n      CHECKED_IF(memaccess.type == expect.type)\n      CHECKED_IF(memaccess.flags == expect.flags)\n      expect.see = true;\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\n// void setFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword hvalue,\n//             QBDI::rword lvalue) {\n//   reinterpret_cast<QBDI::rword *>(fpr)[index * 2] = lvalue;\n//   reinterpret_cast<QBDI::rword *>(fpr)[index * 2 + 1] = hvalue;\n// }\n//\n// void checkFullFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword hvalue,\n//                   QBDI::rword lvalue) {\n//   INFO(\"v\" << index);\n//   CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2] == lvalue);\n//   CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2 + 1] == hvalue);\n// }\n//\n// void checkLowFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword lvalue) {\n//   INFO(\"v\" << index);\n//   CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2] == lvalue);\n// }\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrt_post_imm\") {\n\n  const char source[] = \"ldrt r1, [r0], #4\\n\";\n\n  QBDI::rword v = 0x747f4b7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRT_POST_IMM\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 4);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strt_post_imm\") {\n\n  const char source[] = \"strt r1, [r0], #4\\n\";\n\n  QBDI::rword v = 0x747f4b7e;\n  QBDI::rword dest = 0;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&dest, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRT_POST_IMM\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&dest);\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&dest) + 4);\n  CHECK(dest == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_post_imm\") {\n\n  const char source[] = \"ldrb r1, [r0], #1\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRB_POST_IMM\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 1);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_post_imm\") {\n\n  const char source[] = \"strb r1, [r0], #1\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword dest = 0;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&dest, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRB_POST_IMM\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&dest);\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&dest) + 1);\n  CHECK(dest == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_pre_imm1\") {\n\n  const char source[] = \"ldrb r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRB_PRE_IMM\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_pre_imm2\") {\n\n  const char source[] = \"ldrb r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRB_PRE_IMM\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_pre_imm1\") {\n\n  const char source[] = \"strb r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRB_PRE_IMM\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_pre_imm2\") {\n\n  const char source[] = \"strb r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRB_PRE_IMM\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_pre_reg1\") {\n\n  const char source[] = \"ldrb r1, [r0, r2]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRB_PRE_REG\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_pre_reg2\") {\n\n  const char source[] = \"ldrb r1, [r0, -r2]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRB_PRE_REG\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = 25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_pre_reg3\") {\n\n  const char source[] = \"ldrb r1, [r0, r2, lsl #4]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRB_PRE_REG\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 16;\n  state->r1 = 0;\n  state->r2 = -1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_pre_reg4\") {\n\n  const char source[] = \"ldrb r1, [r0, -r2, lsl #4]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRB_PRE_REG\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 16;\n  state->r1 = 0;\n  state->r2 = 1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_pre_reg1\") {\n\n  const char source[] = \"strb r1, [r0, r2]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRB_PRE_REG\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_pre_reg2\") {\n\n  const char source[] = \"strb r1, [r0, -r2]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRB_PRE_REG\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  state->r2 = 25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_pre_reg3\") {\n\n  const char source[] = \"strb r1, [r0, r2, lsl #4]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRB_PRE_REG\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 16;\n  state->r1 = v;\n  state->r2 = -1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_pre_reg4\") {\n\n  const char source[] = \"strb r1, [r0, -r2, lsl #4]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRB_PRE_REG\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 16;\n  state->r1 = v;\n  state->r2 = 1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_imm1\") {\n\n  const char source[] = \"ldrb r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_imm2\") {\n\n  const char source[] = \"ldrb r1, [r0, #-25]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_imm3\") {\n\n  const char source[] = \"ldrb r1, [pc, #-6]\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v = 0x5f;\n  ExpectedMemoryAccesses expectedPre = {{\n      {codeAddr + 2, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_imm1\") {\n\n  const char source[] = \"strb r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRBi12\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_reg1\") {\n\n  const char source[] = \"ldrb r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBrs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_reg2\") {\n\n  const char source[] = \"ldrb r1, [r0, -r2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBrs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = 25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_reg3\") {\n\n  const char source[] = \"ldrb r1, [r0, r2, lsl #4]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBrs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 16;\n  state->r1 = 0;\n  state->r2 = -1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_reg4\") {\n\n  const char source[] = \"ldrb r1, [r0, -r2, lsl #4]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBrs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 16;\n  state->r1 = 0;\n  state->r2 = 1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrb_reg5\") {\n\n  const char source[] = \"ldrb r1, [pc, -r0]\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRBrs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = codeAddr + 8 - reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_reg1\") {\n\n  const char source[] = \"strb r1, [r0, -r2, lsl #4]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRBrs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 16;\n  state->r1 = v;\n  state->r2 = 1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strb_reg2\") {\n\n  const char source[] = \"strb r1, [pc, -r0]\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRBrs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = codeAddr + 8 - reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_imm1\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, #5]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_imm2\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, #-25]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_imm3\") {\n\n  const char source[] = \"ldrd r2, r3, [pc, #-8];\\n bx lr;\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {codeAddr, ((QBDI::rword *)codeAddr)[0], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {codeAddr + 4, ((QBDI::rword *)codeAddr)[1], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strd_imm1\") {\n\n  const char source[] = \"strd r2, r3, [r0, #-25]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_reg1\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, r1]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_reg2\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, -r1]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 25;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_reg3\") {\n\n  const char source[] = \"ldrd r2, r3, [pc, r0];\\n bx lr;\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {codeAddr, ((QBDI::rword *)codeAddr)[0], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {codeAddr + 4, ((QBDI::rword *)codeAddr)[1], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = -8;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_reg4\") {\n\n  const char source[] = \"ldrd r2, r3, [pc, -r0];\\n bx lr;\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {codeAddr, ((QBDI::rword *)codeAddr)[0], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {codeAddr + 4, ((QBDI::rword *)codeAddr)[1], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = 8;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strd_reg1\") {\n\n  const char source[] = \"strd r2, r3, [pc, -r0]\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRD\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = codeAddr + 8 - ((QBDI::rword)&v1[0]);\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_pre_reg1\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, r1]!\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strd_pre_reg1\") {\n\n  const char source[] = \"strd r2, r3, [r0, r1]!\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRD_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = 5;\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrd_post_reg1\") {\n\n  const char source[] = \"ldrd r2, r3, [r0], r1\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRD_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 5;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strd_post_reg1\") {\n\n  const char source[] = \"strd r2, r3, [r0], r1\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRD_POST\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 5;\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrh_imm1\") {\n\n  const char source[] = \"ldrh r2, [r0, #5]\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRH\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strh_imm1\") {\n\n  const char source[] = \"strh r2, [r0, #5]\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRH\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrh_reg1\") {\n\n  const char source[] = \"ldrh r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRH\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strh_reg1\") {\n\n  const char source[] = \"strh r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRH\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrh_pre_reg1\") {\n\n  const char source[] = \"ldrh r2, [r0, r1]!\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRH_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strh_pre_reg1\") {\n\n  const char source[] = \"strh r2, [r0, r1]!\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRH_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrh_post_reg1\") {\n\n  const char source[] = \"ldrh r2, [r0], r1\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRH_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-strh_post_reg1\") {\n\n  const char source[] = \"strh r2, [r0], r1\\n\";\n\n  QBDI::rword v = 0xa3c1;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRH_POST\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrsh_reg1\") {\n\n  const char source[] = \"ldrsh r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0x23c1;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRSH\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrsh_pre_reg1\") {\n\n  const char source[] = \"ldrsh r2, [r0, r1]!\\n\";\n\n  QBDI::rword v = 0x23c1;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRSH_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrsh_post_reg1\") {\n\n  const char source[] = \"ldrsh r2, [r0], r1\\n\";\n\n  QBDI::rword v = 0x23c1;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRSH_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrsb_reg1\") {\n\n  const char source[] = \"ldrsb r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0x23;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRSB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrsb_pre_reg1\") {\n\n  const char source[] = \"ldrsb r2, [r0, r1]!\\n\";\n\n  QBDI::rword v = 0x23;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRSB_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldrsb_post_reg1\") {\n\n  const char source[] = \"ldrsb r2, [r0], r1\\n\";\n\n  QBDI::rword v = 0x23;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRSB_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_imm1\") {\n\n  const char source[] = \"ldr r2, [r0, #5]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_imm2\") {\n\n  const char source[] = \"ldr r2, [r0, #-25]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-str_imm1\") {\n\n  const char source[] = \"str r2, [r0, #5]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRi12\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_reg1\") {\n\n  const char source[] = \"ldr r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRrs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_reg2\") {\n\n  const char source[] = \"ldr r2, [r0, -r1, lsr #4]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDRrs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = (25 << 4);\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-str_reg1\") {\n\n  const char source[] = \"str r2, [r0, -r1, lsr #4]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STRrs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = (25 << 4);\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_pre_imm1\") {\n\n  const char source[] = \"ldr r2, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDR_PRE_IMM\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-str_pre_imm1\") {\n\n  const char source[] = \"str r2, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STR_PRE_IMM\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_pre_reg1\") {\n\n  const char source[] = \"ldr r2, [r0, r1]!\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDR_PRE_REG\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_pre_reg2\") {\n\n  const char source[] = \"ldr r2, [r0, r1, lsr #4]!\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDR_PRE_REG\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = (5 << 4);\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-str_pre_reg1\") {\n\n  const char source[] = \"str r2, [r0, r1, lsr #4]!\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STR_PRE_REG\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = (5 << 4);\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_post_imm1\") {\n\n  const char source[] = \"ldr r2, [r0], 25\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDR_POST_IMM\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-str_post_imm1\") {\n\n  const char source[] = \"str r2, [r0], 25\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STR_POST_IMM\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldr_post_reg1\") {\n\n  const char source[] = \"ldr r2, [r0], r1\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDR_POST_REG\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-str_post_reg1\") {\n\n  const char source[] = \"str r2, [r0], r1\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STR_POST_REG\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n"
  },
  {
    "path": "test/API/ARM/MemoryAccessTest_ARM_LDM_STM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#include \"Utility/System.h\"\n\n[[maybe_unused]] static QBDI::VMAction debugCB(QBDI::VMInstanceRef vm,\n                                               QBDI::GPRState *gprState,\n                                               QBDI::FPRState *fprState,\n                                               void *data) {\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  printf(\"0x%x (%10s): %s\\n\", instAnalysis->address, instAnalysis->mnemonic,\n         instAnalysis->disassembly);\n\n  for (auto &a : vm->getInstMemoryAccess()) {\n    printf(\n        \" - inst: 0x%x, addr: 0x%x, size: %d, type: %c%c, \"\n        \"value: 0x%x, flags : 0x %x\\n\",\n        a.instAddress, a.accessAddress, a.size,\n        ((a.type & QBDI::MEMORY_READ) != 0) ? 'r' : '-',\n        ((a.type & QBDI::MEMORY_WRITE) != 0) ? 'w' : '-', a.value, a.flags);\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nstruct ExpectedMemoryAccess {\n  QBDI::rword address;\n  QBDI::rword value;\n  uint16_t size;\n  QBDI::MemoryAccessType type;\n  QBDI::MemoryAccessFlags flags;\n  bool see = false;\n};\n\nstruct ExpectedMemoryAccesses {\n  std::vector<ExpectedMemoryAccess> accesses;\n};\n\nstatic QBDI::VMAction checkAccess(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n\n  ExpectedMemoryAccesses *info = static_cast<ExpectedMemoryAccesses *>(data);\n  if (std::all_of(info->accesses.begin(), info->accesses.end(),\n                  [](ExpectedMemoryAccess &a) { return a.see; }))\n    return QBDI::VMAction::CONTINUE;\n\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n\n  CHECKED_IF(memaccesses.size() == info->accesses.size()) {\n    for (size_t i = 0; i < info->accesses.size(); i++) {\n      auto &memaccess = memaccesses[i];\n      auto &expect = info->accesses[i];\n      INFO(\"Expected Access n°\" << i);\n      INFO(\"Value 0x\" << std::hex << memaccess.value << \" expect 0x\" << std::hex\n                      << expect.value);\n      CHECKED_IF(memaccess.accessAddress == expect.address)\n      CHECKED_IF((memaccess.value == expect.value || expect.value == 0))\n      CHECKED_IF(memaccess.size == expect.size)\n      CHECKED_IF(memaccess.type == expect.type)\n      CHECKED_IF(memaccess.flags == expect.flags)\n      expect.see = true;\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\n// LDMIA\n// =====\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmia1\") {\n\n  const char source[] = \"ldmia r0, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmia2\") {\n\n  const char source[] = \"ldmia r0, {r0-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xc2708a8b, 0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[15], v1[15], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 16; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmia3\") {\n\n  const char source[] = \"cmp r1, #42; ldmiane r0, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmia_post1\") {\n\n  const char source[] = \"ldmia r0!, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmia_post2\") {\n\n  const char source[] = \"ldmia r0!, {r1-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 15; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmia_post3\") {\n\n  const char source[] = \"cmp r1, #42; ldmiane r0!, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\n// LDMIB\n// =====\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmib1\") {\n\n  const char source[] = \"ldmib r0, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmib2\") {\n\n  const char source[] = \"ldmib r0, {r0-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xc2708a8b, 0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[15], v1[15], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 16; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmib3\") {\n\n  const char source[] = \"cmp r1, #42; ldmibne r0, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmib_post1\") {\n\n  const char source[] = \"ldmib r0!, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmib_post2\") {\n\n  const char source[] = \"ldmib r0!, {r1-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 15; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmib_post3\") {\n\n  const char source[] = \"cmp r1, #42; ldmibne r0!, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMIB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\n// LDMDA\n// =====\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmda1\") {\n\n  const char source[] = \"ldmda r0, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmda2\") {\n\n  const char source[] = \"ldmda r0, {r0-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xc2708a8b, 0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[15], v1[15], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 15;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 16; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmda3\") {\n\n  const char source[] = \"cmp r1, #42; ldmdane r0, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmda_post1\") {\n\n  const char source[] = \"ldmda r0!, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmda_post2\") {\n\n  const char source[] = \"ldmda r0!, {r1-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 14;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 15; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmda_post3\") {\n\n  const char source[] = \"cmp r1, #42; ldmdane r0!, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\n// LDMDB\n// =====\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmdb1\") {\n\n  const char source[] = \"ldmdb r0, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmdb2\") {\n\n  const char source[] = \"ldmdb r0, {r0-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xc2708a8b, 0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[15], v1[15], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 16;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 16; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmdb3\") {\n\n  const char source[] = \"cmp r1, #42; ldmdbne r0, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmdb_post1\") {\n\n  const char source[] = \"ldmdb r0!, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmdb_post2\") {\n\n  const char source[] = \"ldmdb r0!, {r1-r12,sp,lr,pc}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2d624053, 0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[14], v1[14], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 15;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (int i = 0; i < 15; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-ldmdb_post3\") {\n\n  const char source[] = \"cmp r1, #42; ldmdbne r0!, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LDMDB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmia1\") {\n\n  const char source[] = \"stmia r0, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmia2\") {\n\n  const char source[] = \"stmia r0, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmia3\") {\n\n  const char source[] = \"cmp r3, #42; stmiane r0, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  // with condition reach\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 12};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n\n  // with condition not reach\n  expectedPost = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0] + 1;\n  state->r2 = v[1] + 1;\n  state->r3 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmia_post1\") {\n\n  const char source[] = \"stmia r0!, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmia_post2\") {\n\n  const char source[] = \"stmia r0!, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmib1\") {\n\n  const char source[] = \"stmib r0, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmib2\") {\n\n  const char source[] = \"stmib r0, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmib_post1\") {\n\n  const char source[] = \"stmib r0!, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIB_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmib_post2\") {\n\n  const char source[] = \"stmib r0!, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMIB_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmda1\") {\n\n  const char source[] = \"stmda r0, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmda2\") {\n\n  const char source[] = \"stmda r0, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmda_post1\") {\n\n  const char source[] = \"stmda r0!, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmda_post2\") {\n\n  const char source[] = \"stmda r0!, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmdb1\") {\n\n  const char source[] = \"stmdb r0, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmdb2\") {\n\n  const char source[] = \"stmdb r0, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmdb_post1\") {\n\n  const char source[] = \"stmdb r0!, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDB_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-stmdb_post2\") {\n\n  const char source[] = \"stmdb r0!, {r1, r2, pc}\\n\";\n  QBDI::rword codeAddr = genASM(source);\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, codeAddr + 8};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STMDB_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vldmdia1\") {\n\n  const char source[] = \"vldmia r0, {d0, d1}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369, 0xb25e4516};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VLDMDIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  CHECK(memcmp(fpr, &v1, sizeof(v1)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vldmdia2\") {\n\n  const char source[] = \"vldmia r0!, {d0-d15}\\n\";\n\n  QBDI::rword v1[32] = {0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], 0, 128, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VLDMDIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  CHECK(memcmp(fpr, &v1, sizeof(v1)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vldmsia1\") {\n\n  const char source[] = \"vldmia r0, {s0-s2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VLDMSIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  CHECK(memcmp(fpr, &v1, sizeof(v1)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vldmsia2\") {\n\n  const char source[] = \"vldmia r0!, {s0-s31}\\n\";\n\n  QBDI::rword v1[32] = {0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], 0, 128, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VLDMSIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  CHECK(memcmp(fpr, &v1, sizeof(v1)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vstmdia1\") {\n\n  const char source[] = \"vstmia r0, {d0, d1}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369, 0xb25e4516};\n  QBDI::rword v1[] = {0, 0, 0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v[3], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VSTMDIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  memcpy(fpr, v, sizeof(v));\n  vm.setFPRState(fpr);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(&v, &v1, sizeof(v1)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vstmdia2\") {\n\n  const char source[] = \"vstmia r0!, {d0-d15}\\n\";\n\n  QBDI::rword v[32] = {\n      0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b, 0x215f4510,\n      0x927e556e, 0xa8a0e729, 0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n      0xc2708a8b, 0x2d624053, 0xaad33b87, 0x33562724, 0x215f4510, 0x927e556e,\n      0xa8a0e729, 0x235b2fc3, 0xc2708a8b, 0x2d624053, 0xaad33b87, 0x33562724,\n      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x319b8e1b, 0x31eed260, 0x747f4b7e,\n      0xc2708a8b, 0xf21a4416};\n  QBDI::rword v1[32] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], 0, 128, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VSTMDIA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  memcpy(fpr, v, sizeof(v));\n  vm.setFPRState(fpr);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(&v, &v1, sizeof(v1)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vstmsia1\") {\n\n  const char source[] = \"vstmia r0, {s0-s2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VSTMSIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  memcpy(fpr, v, sizeof(v));\n  vm.setFPRState(fpr);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(&v, &v1, sizeof(v1)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_ARM-vstmsia2\") {\n\n  const char source[] = \"vstmia r0!, {s0-s31}\\n\";\n\n  QBDI::rword v[32] = {\n      0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b, 0x215f4510,\n      0x927e556e, 0xa8a0e729, 0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n      0xc2708a8b, 0x2d624053, 0xaad33b87, 0x33562724, 0x215f4510, 0x927e556e,\n      0xa8a0e729, 0x235b2fc3, 0xc2708a8b, 0x2d624053, 0xaad33b87, 0x33562724,\n      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x319b8e1b, 0x31eed260, 0x747f4b7e,\n      0xc2708a8b, 0xf21a4416};\n  QBDI::rword v1[32] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], 0, 128, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"VSTMSIA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fpr = vm.getFPRState();\n  memcpy(fpr, v, sizeof(v));\n  vm.setFPRState(fpr);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(&v, &v1, sizeof(v1)) == 0);\n}\n"
  },
  {
    "path": "test/API/ARM/MemoryAccessTest_Thumb.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#include \"Utility/System.h\"\n\n[[maybe_unused]] static bool checkFeature(const char *f) {\n  if (!QBDI::isHostCPUFeaturePresent(f)) {\n    WARN(\"Host doesn't support \" << f << \" feature: SKIP\");\n    return false;\n  }\n  return true;\n}\n\n[[maybe_unused]] static QBDI::VMAction debugCB(QBDI::VMInstanceRef vm,\n                                               QBDI::GPRState *gprState,\n                                               QBDI::FPRState *fprState,\n                                               void *data) {\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  printf(\"0x%x (%10s): %s\\n\", instAnalysis->address, instAnalysis->mnemonic,\n         instAnalysis->disassembly);\n\n  for (auto &a : vm->getInstMemoryAccess()) {\n    printf(\n        \" - inst: 0x%x, addr: 0x%x, size: %d, type: %c%c, \"\n        \"value: 0x%x, flags : 0x %x\\n\",\n        a.instAddress, a.accessAddress, a.size,\n        ((a.type & QBDI::MEMORY_READ) != 0) ? 'r' : '-',\n        ((a.type & QBDI::MEMORY_WRITE) != 0) ? 'w' : '-', a.value, a.flags);\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nstruct ExpectedMemoryAccess {\n  QBDI::rword address;\n  QBDI::rword value;\n  uint16_t size;\n  QBDI::MemoryAccessType type;\n  QBDI::MemoryAccessFlags flags;\n  bool see = false;\n};\n\nstruct ExpectedMemoryAccesses {\n  std::vector<ExpectedMemoryAccess> accesses;\n};\n\nstatic QBDI::VMAction checkAccess(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n\n  ExpectedMemoryAccesses *info = static_cast<ExpectedMemoryAccesses *>(data);\n  if (std::all_of(info->accesses.begin(), info->accesses.end(),\n                  [](ExpectedMemoryAccess &a) { return a.see; }))\n    return QBDI::VMAction::CONTINUE;\n\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n\n  CHECKED_IF(memaccesses.size() == info->accesses.size()) {\n    for (size_t i = 0; i < info->accesses.size(); i++) {\n      auto &memaccess = memaccesses[i];\n      auto &expect = info->accesses[i];\n      INFO(\"Expected Access n°\" << i);\n      INFO(\"Value 0x\" << std::hex << memaccess.value << \" expect 0x\" << std::hex\n                      << expect.value);\n      CHECKED_IF(memaccess.accessAddress == expect.address)\n      CHECKED_IF((memaccess.value == expect.value || expect.value == 0))\n      CHECKED_IF(memaccess.size == expect.size)\n      CHECKED_IF(memaccess.type == expect.type)\n      CHECKED_IF(memaccess.flags == expect.flags)\n      expect.see = true;\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\n// void setFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword hvalue,\n//             QBDI::rword lvalue) {\n//   reinterpret_cast<QBDI::rword *>(fpr)[index * 2] = lvalue;\n//   reinterpret_cast<QBDI::rword *>(fpr)[index * 2 + 1] = hvalue;\n// }\n//\n// void checkFullFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword hvalue,\n//                   QBDI::rword lvalue) {\n//   INFO(\"v\" << index);\n//   CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2] == lvalue);\n//   CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2 + 1] == hvalue);\n// }\n//\n// void checkLowFPR(QBDI::FPRState *fpr, size_t index, QBDI::rword lvalue) {\n//   INFO(\"v\" << index);\n//   CHECK(reinterpret_cast<QBDI::rword *>(fpr)[index * 2] == lvalue);\n// }\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrt\") {\n\n  const char source[] = \"ldrt r1, [r0, #4]\\n\";\n\n  QBDI::rword v = 0x747f4b7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRT\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 4;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) - 4);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strt\") {\n\n  const char source[] = \"strt r1, [r0, #4]\\n\";\n\n  QBDI::rword v = 0x747f4b7e;\n  QBDI::rword dest = 0;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&dest, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRT\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&dest) - 4;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&dest) - 4);\n  CHECK(dest == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrbt\") {\n\n  const char source[] = \"ldrbt r1, [r0, #4]\\n\";\n\n  QBDI::rword v = 0x74;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRBT\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 4;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) - 4);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strbt\") {\n\n  const char source[] = \"strbt r1, [r0, #4]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword dest = 0;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&dest, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRBT\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&dest) - 4;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&dest) - 4);\n  CHECK(dest == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_post\") {\n\n  const char source[] = \"ldrb r1, [r0], #1\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRB_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 1);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_post\") {\n\n  const char source[] = \"strb r1, [r0], #1\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword dest = 0;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&dest, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRB_POST\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&dest);\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&dest) + 1);\n  CHECK(dest == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_pre1\") {\n\n  const char source[] = \"ldrb r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRB_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_pre2\") {\n\n  const char source[] = \"ldrb r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRB_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_pre1\") {\n\n  const char source[] = \"strb r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRB_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_pre2\") {\n\n  const char source[] = \"strb r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRB_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_imm1\") {\n\n  const char source[] = \"ldrb r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRBi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_imm2\") {\n\n  const char source[] = \"ldrb.w r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRBi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_imm3\") {\n\n  const char source[] = \"ldrb r1, [r0, #-25]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRBi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_imm4\") {\n\n  const char source[] = \"ldrb r1, [pc, #-1]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x10;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRBpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_imm5\") {\n\n  const char source[] = \"nop\\n ldrb r1, [pc, #-1]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0xf8;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRBpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_imm1\") {\n\n  const char source[] = \"strb r1, [r0, #524]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRBi12\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 524;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_imm2\") {\n\n  const char source[] = \"strb r1, [r0, #-57]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRBi8\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 57;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_imm3\") {\n\n  const char source[] = \"strb r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTRBi\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_reg1\") {\n\n  const char source[] = \"ldrb r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRBr\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_reg2\") {\n\n  const char source[] = \"ldrb.w r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRBs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrb_reg3\") {\n\n  const char source[] = \"ldrb.w r1, [r0, r2, lsl #2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRBs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 24;\n  state->r1 = 0;\n  state->r2 = -6;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 24);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_reg1\") {\n\n  const char source[] = \"strb r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTRBr\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 25);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_reg2\") {\n\n  const char source[] = \"strb.w r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRBs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 25);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strb_reg3\") {\n\n  const char source[] = \"strb.w r1, [r0, r2, lsl #2]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRBs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 24;\n  state->r1 = v;\n  state->r2 = -6;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 24);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrd_imm1\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, #4]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRDi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 4;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrd_imm2\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, #-24]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRDi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 24;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrd_imm3\") {\n\n  const char source[] = \"ldrd r2, r3, [pc, #-4];\\n bx lr;\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)), ((QBDI::rword *)(codeAddr & (~1)))[0], 4,\n       QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(codeAddr & (~1)) + 4, ((QBDI::rword *)(codeAddr & (~1)))[1], 4,\n       QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRDi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  CHECK(state->r2 == expectedPre.accesses[0].value);\n  CHECK(state->r3 == expectedPre.accesses[1].value);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strd_imm1\") {\n\n  const char source[] = \"strd r2, r3, [r0, #56]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRDi8\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 56;\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strd_imm2\") {\n\n  const char source[] = \"strd r2, r3, [r0, #-24]\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRDi8\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 24;\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrd_pre1\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, #4]!\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRD_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 4;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrd_pre2\") {\n\n  const char source[] = \"ldrd r2, r3, [r0, #-24]!\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRD_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 24;\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strd_pre1\") {\n\n  const char source[] = \"strd r2, r3, [r0, #56]!\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRD_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 56;\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strd_pre2\") {\n\n  const char source[] = \"strd r2, r3, [r0, #-24]!\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRD_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 24;\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrd_post1\") {\n\n  const char source[] = \"ldrd r2, r3, [r0], #4\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRD_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 4);\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrd_post2\") {\n\n  const char source[] = \"ldrd r2, r3, [r0], #-24\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRD_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r2 = 0;\n  state->r3 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) - 24);\n  CHECK(state->r2 == v[0]);\n  CHECK(state->r3 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strd_post1\") {\n\n  const char source[] = \"strd r2, r3, [r0], #56\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRD_POST\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 56);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strd_post2\") {\n\n  const char source[] = \"strd r2, r3, [r0], #-24\\n\";\n\n  QBDI::rword v[] = {0x719ca3c1, 0xd7b24369};\n  QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRD_POST\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r2 = v[0];\n  state->r3 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) - 24);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_post\") {\n\n  const char source[] = \"ldrh r1, [r0], #45\\n\";\n\n  QBDI::rword v = 0x7e95;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRH_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 45);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_post\") {\n\n  const char source[] = \"strh r1, [r0], #25\\n\";\n\n  QBDI::rword v = 0x7e75;\n  QBDI::rword dest = 0;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&dest, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRH_POST\", QBDI::POSTINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&dest);\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&dest) + 25);\n  CHECK(dest == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_pre1\") {\n\n  const char source[] = \"ldrh r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x7e13;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRH_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_pre2\") {\n\n  const char source[] = \"ldrh r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x7e11;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRH_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_pre1\") {\n\n  const char source[] = \"strh r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x7e74;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRH_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_pre2\") {\n\n  const char source[] = \"strh r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x7e75;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRH_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_imm1\") {\n\n  const char source[] = \"ldrh r1, [r0, #6]\\n\";\n\n  QBDI::rword v = 0x7e54;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRHi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 6;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_imm2\") {\n\n  const char source[] = \"ldrh.w r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0x76fe;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRHi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_imm3\") {\n\n  const char source[] = \"ldrh r1, [r0, #-25]\\n\";\n\n  QBDI::rword v = 0xda7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRHi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_imm4\") {\n\n  const char source[] = \"ldrh r1, [pc, #-1]\\n bx lr\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x7010;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRHpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_imm5\") {\n\n  const char source[] = \"nop\\n ldrh r1, [pc, #-1]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x01f8;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRHpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_imm1\") {\n\n  const char source[] = \"strh r1, [r0, #524]\\n\";\n\n  QBDI::rword v = 0xb86e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRHi12\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 524;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_imm2\") {\n\n  const char source[] = \"strh r1, [r0, #-57]\\n\";\n\n  QBDI::rword v = 0x138f;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRHi8\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 57;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_imm3\") {\n\n  const char source[] = \"strh r1, [r0, #6]\\n\";\n\n  QBDI::rword v = 0x74d2;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTRHi\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 6;\n  state->r1 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_reg1\") {\n\n  const char source[] = \"ldrh r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x7e43;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRHr\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_reg2\") {\n\n  const char source[] = \"ldrh.w r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x437e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRHs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrh_reg3\") {\n\n  const char source[] = \"ldrh.w r1, [r0, r2, lsl #2]\\n\";\n\n  QBDI::rword v = 0x7e2e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRHs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 24;\n  state->r1 = 0;\n  state->r2 = -6;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 24);\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_reg1\") {\n\n  const char source[] = \"strh r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x746e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTRHr\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 25);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_reg2\") {\n\n  const char source[] = \"strh.w r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0xf254;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRHs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = v;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 25);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strh_reg3\") {\n\n  const char source[] = \"strh.w r1, [r0, r2, lsl #2]\\n\";\n\n  QBDI::rword v = 0xa68b;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRHs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 24;\n  state->r1 = v;\n  state->r2 = -6;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 24);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_post\") {\n\n  const char source[] = \"ldrsb r1, [r0], #1\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSB_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 1);\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_pre1\") {\n\n  const char source[] = \"ldrsb r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSB_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_pre2\") {\n\n  const char source[] = \"ldrsb r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSB_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_imm1\") {\n\n  const char source[] = \"ldrsb.w r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSBi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_imm2\") {\n\n  const char source[] = \"ldrsb r1, [r0, #-25]\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSBi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_imm3\") {\n\n  const char source[] = \"ldrsb r1, [pc, #-1]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x10;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSBpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_imm4\") {\n\n  const char source[] = \"nop\\n ldrsb r1, [pc, #-1]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0xf9;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSBpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_reg1\") {\n\n  const char source[] = \"ldrsb r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRSBr\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_reg2\") {\n\n  const char source[] = \"ldrsb.w r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSBs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsb_reg3\") {\n\n  const char source[] = \"ldrsb.w r1, [r0, r2, lsl #2]\\n\";\n\n  QBDI::rword v = 0x8e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSBs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 24;\n  state->r1 = 0;\n  state->r2 = -6;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 24);\n  CHECK(((QBDI::sword)state->r1) == ((int8_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_post\") {\n\n  const char source[] = \"ldrsh r1, [r0], #45\\n\";\n\n  QBDI::rword v = 0x9e95;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSH_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 45);\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_pre1\") {\n\n  const char source[] = \"ldrsh r1, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0x9e13;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSH_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_pre2\") {\n\n  const char source[] = \"ldrsh r1, [r0, #-25]!\\n\";\n\n  QBDI::rword v = 0x9e11;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSH_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v));\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_imm1\") {\n\n  const char source[] = \"ldrsh.w r1, [r0, #5]\\n\";\n\n  QBDI::rword v = 0xa6fe;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSHi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_imm2\") {\n\n  const char source[] = \"ldrsh r1, [r0, #-25]\\n\";\n\n  QBDI::rword v = 0xda7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSHi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_imm3\") {\n\n  const char source[] = \"ldrsh r1, [pc, #-1]\\n bx lr\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x7010;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSHpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_imm4\") {\n\n  const char source[] = \"nop\\n ldrsh r1, [pc, #-1]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x01f9;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 3, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSHpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_reg1\") {\n\n  const char source[] = \"ldrsh r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0xfe43;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRSHr\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_reg2\") {\n\n  const char source[] = \"ldrsh.w r1, [r0, r2]\\n\";\n\n  QBDI::rword v = 0x937e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSHs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r1 = 0;\n  state->r2 = -25;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 25);\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrsh_reg3\") {\n\n  const char source[] = \"ldrsh.w r1, [r0, r2, lsl #2]\\n\";\n\n  QBDI::rword v = 0xce2e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRSHs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 24;\n  state->r1 = 0;\n  state->r2 = -6;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v) + 24);\n  CHECK(((QBDI::sword)state->r1) == ((int16_t)v));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm1\") {\n\n  const char source[] = \"ldr r2, [r0, #4]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 4;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm2\") {\n\n  const char source[] = \"mov sp, r0\\n ldr r2, [sp, #4]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRspi\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 4;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm3\") {\n\n  const char source[] = \"ldr.w r2, [r0, #5]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRi12\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm4\") {\n\n  const char source[] = \"ldr r2, [r0, #-25]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRi8\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) + 25;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm5\") {\n\n  const char source[] =\n      \"ldr r1, [pc, #4]\\n bx lr\\n.long 0xfe125498\\n.long 0x5eb6a7ce\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x5eb6a7ce;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 8, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm6\") {\n\n  const char source[] =\n      \"nop\\nldr r1, [pc, #4]\\n bx lr\\n.long 0xfe125498\\n.long 0x5eb6a7ce\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0xa7cefe12;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)) + 8, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm7\") {\n\n  const char source[] = \"ldr r1, [pc, #-4]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0x1004f85f;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)), v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_imm8\") {\n\n  const char source[] = \"nop\\n ldr r1, [pc, #-4]\\n\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n  REQUIRE(codeAddr % 4 == 1);\n\n  QBDI::rword v = 0xf85fbf00;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(codeAddr & (~1)), v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRpci\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, codeAddr, {});\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_imm1\") {\n\n  const char source[] = \"str r2, [r0, #4]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTRi\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 4;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_imm2\") {\n\n  const char source[] = \"mov sp, r0\\n str r2, [sp, #8]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTRspi\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 8;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_imm3\") {\n\n  const char source[] = \"str.w r2, [r0, #35]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRi12\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 35;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_imm4\") {\n\n  const char source[] = \"str r2, [r0, #-43]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRi8\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 43;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_reg1\") {\n\n  const char source[] = \"ldr r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDRr\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_reg2\") {\n\n  const char source[] = \"ldr.w r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r1 = 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_reg3\") {\n\n  const char source[] = \"ldr r2, [r0, r1, lsl #3]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDRs\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 24;\n  state->r1 = 3;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_reg1\") {\n\n  const char source[] = \"str r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTRr\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 25;\n  state->r1 = 25;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_reg2\") {\n\n  const char source[] = \"str.w r2, [r0, r1]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 25;\n  state->r1 = -25;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_reg3\") {\n\n  const char source[] = \"str r2, [r0, r1, lsl #3]\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STRs\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 24;\n  state->r1 = 3;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_pre_imm1\") {\n\n  const char source[] = \"ldr r2, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDR_PRE\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 5;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_pre_imm1\") {\n\n  const char source[] = \"str r2, [r0, #5]!\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STR_PRE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 5;\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldr_post_imm1\") {\n\n  const char source[] = \"ldr r2, [r0], 25\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDR_POST\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r2 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-str_post_imm1\") {\n\n  const char source[] = \"str r2, [r0], 25\\n\";\n\n  QBDI::rword v = 0xb25e4516;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STR_POST\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r2 = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrexb\") {\n\n  const char source[] = \"ldrexb r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDREXB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrexd\") {\n\n  const char source[] = \"ldrexd r1, r2, [r0]\\n\";\n\n  alignas(16) QBDI::rword v[] = {0x13eb7e46, 0x4a56ebc9};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v[0], v[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v[1], v[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDREXD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  state->r2 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v[0]);\n  CHECK(state->r2 == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrexh\") {\n\n  const char source[] = \"ldrexh r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e46;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDREXH\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrex1\") {\n\n  const char source[] = \"ldrex r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e4e5686;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDREX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrex2\") {\n\n  const char source[] = \"ldrex r1, [r0, #8]\\n\";\n\n  QBDI::rword v = 0x7e4e5686;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDREX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 8;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-ldrex3\") {\n\n  const char source[] = \"ldrex r1, [r0, #1020]\\n\";\n\n  QBDI::rword v = 0x7e4e5686;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, v, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDREX\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v) - 1020;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strexb1\") {\n\n  const char source[] = \"strexb r10, r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREXB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v;\n  state->localMonitor.enable = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strexb2\") {\n\n  const char source[] = \"strexb r10, r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREXB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v;\n  state->localMonitor.enable = 1;\n  state->localMonitor.addr = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strexd1\") {\n\n  const char source[] = \"strexd r10, r1, r2, [r0]\\n\";\n\n  alignas(16) QBDI::rword v[] = {0x7112a12e, 0xb0e356d9};\n  alignas(16) QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], 0, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], 0, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREXD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->localMonitor.enable = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1[0] == 0);\n  CHECK(v1[1] == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strexd2\") {\n\n  const char source[] = \"strexd r10, r1, r2, [r0]\\n\";\n\n  alignas(16) QBDI::rword v[] = {0x7112a12e, 0xb0e356d9};\n  alignas(16) QBDI::rword v1[] = {0, 0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREXD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->localMonitor.enable = 8;\n  state->localMonitor.addr = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1[0] == v[0]);\n  CHECK(v1[1] == v[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strexh1\") {\n\n  const char source[] = \"strexh r10, r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e2e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREXH\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v;\n  state->localMonitor.enable = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strexh2\") {\n\n  const char source[] = \"strexh r10, r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e13;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREXH\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v;\n  state->localMonitor.enable = 2;\n  state->localMonitor.addr = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strex1\") {\n\n  const char source[] = \"strex r10, r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7802d13e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREX\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v;\n  state->localMonitor.enable = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strex2\") {\n\n  const char source[] = \"strex r10, r1, [r0]\\n\";\n\n  QBDI::rword v = 0x7e40e213;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREX\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v;\n  state->localMonitor.enable = 4;\n  state->localMonitor.addr = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strex3\") {\n\n  const char source[] = \"strex r10, r1, [r0, #8]\\n\";\n\n  QBDI::rword v = 0x7802d13e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREX\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 8;\n  state->r1 = v;\n  state->localMonitor.enable = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strex4\") {\n\n  const char source[] = \"strex r10, r1, [r0, #8]\\n\";\n\n  QBDI::rword v = 0x7e40e213;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREX\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 8;\n  state->r1 = v;\n  state->localMonitor.enable = 4;\n  state->localMonitor.addr = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strex5\") {\n\n  const char source[] = \"strex r10, r1, [r0, #1020]\\n\";\n\n  QBDI::rword v = 0x7802d13e;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREX\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 1020;\n  state->r1 = v;\n  state->localMonitor.enable = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-strex6\") {\n\n  const char source[] = \"strex r10, r1, [r0, #1020]\\n\";\n\n  QBDI::rword v = 0x7e40e213;\n  QBDI::rword v1 = 0;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STREX\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) - 1020;\n  state->r1 = v;\n  state->localMonitor.enable = 4;\n  state->localMonitor.addr = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n  CHECK(v1 == v);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tbb1\") {\n\n  const char source[] =\n      \"tbb [pc, r0]\\n.word 0x05040504\\n.word 0x05040504\\nbx lr\\nbx lr\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {0, 0, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2TBB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  for (int i = 0; i < 8; i++) {\n    QBDI::GPRState *state = vm.getGPRState();\n    state->r0 = i;\n    vm.setGPRState(state);\n\n    expectedPre.accesses[0].see = false;\n    expectedPre.accesses[0].address = (codeAddr & (~1)) + 4 + i;\n    expectedPre.accesses[0].value = ((i & 1) == 1) ? 0x5 : 0x4;\n\n    QBDI::rword retval;\n    bool ran = vm.call(&retval, codeAddr, {});\n\n    CHECK(ran);\n    for (auto &e : expectedPre.accesses)\n      CHECK(e.see);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tbb2\") {\n\n  const char source[] = \"tbb [r0, r1]\\nbx lr\\nbx lr\\nbx lr\\nbx lr\\nbx lr\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n\n  uint8_t v[] = {0, 3, 2, 3, 1, 2, 4, 0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {0, 0, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2TBB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  for (unsigned i = 0; i < sizeof(v); i++) {\n    QBDI::GPRState *state = vm.getGPRState();\n    state->r0 = (QBDI::rword)&v;\n    state->r1 = i;\n    vm.setGPRState(state);\n\n    expectedPre.accesses[0].see = false;\n    expectedPre.accesses[0].address = (QBDI::rword)&v[i];\n    expectedPre.accesses[0].value = v[i];\n\n    QBDI::rword retval;\n    bool ran = vm.call(&retval, codeAddr, {});\n\n    CHECK(ran);\n    for (auto &e : expectedPre.accesses)\n      CHECK(e.see);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tbh1\") {\n\n  const char source[] =\n      \"tbh [pc, r0, lsl #1]\\n.word 0x090008\\n.word 0x090008\\n.word \"\n      \"0x090008\\n.word 0x090008\\nbx lr\\nbx lr\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {0, 0, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2TBH\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  for (int i = 0; i < 8; i++) {\n    QBDI::GPRState *state = vm.getGPRState();\n    state->r0 = i;\n    vm.setGPRState(state);\n\n    expectedPre.accesses[0].see = false;\n    expectedPre.accesses[0].address = (codeAddr & (~1)) + 4 + i * 2;\n    expectedPre.accesses[0].value = ((i & 1) == 1) ? 0x9 : 0x8;\n\n    QBDI::rword retval;\n    bool ran = vm.call(&retval, codeAddr, {});\n\n    CHECK(ran);\n    for (auto &e : expectedPre.accesses)\n      CHECK(e.see);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tbh2\") {\n\n  const char source[] =\n      \"tbh [r0, r1, lsl #1]\\nbx lr\\nbx lr\\nbx lr\\nbx lr\\nbx lr\";\n  QBDI::rword codeAddr = genASM(source, QBDI::CPUMode::Thumb);\n\n  uint16_t v[] = {0, 3, 2, 3, 1, 2, 4, 0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {0, 0, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2TBH\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  for (unsigned i = 0; i < sizeof(v) / sizeof(v[0]); i++) {\n    QBDI::GPRState *state = vm.getGPRState();\n    state->r0 = (QBDI::rword)&v;\n    state->r1 = i;\n    vm.setGPRState(state);\n\n    expectedPre.accesses[0].see = false;\n    expectedPre.accesses[0].address = (QBDI::rword)&v[i];\n    expectedPre.accesses[0].value = v[i];\n\n    QBDI::rword retval;\n    bool ran = vm.call(&retval, codeAddr, {});\n\n    CHECK(ran);\n    for (auto &e : expectedPre.accesses)\n      CHECK(e.see);\n  }\n}\n"
  },
  {
    "path": "test/API/ARM/MemoryAccessTest_Thumb_LDM_STM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#include \"Utility/System.h\"\n\n[[maybe_unused]] static QBDI::VMAction debugCB(QBDI::VMInstanceRef vm,\n                                               QBDI::GPRState *gprState,\n                                               QBDI::FPRState *fprState,\n                                               void *data) {\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis();\n  printf(\"0x%x (%10s): %s\\n\", instAnalysis->address, instAnalysis->mnemonic,\n         instAnalysis->disassembly);\n\n  for (auto &a : vm->getInstMemoryAccess()) {\n    printf(\n        \" - inst: 0x%x, addr: 0x%x, size: %d, type: %c%c, \"\n        \"value: 0x%x, flags : 0x %x\\n\",\n        a.instAddress, a.accessAddress, a.size,\n        ((a.type & QBDI::MEMORY_READ) != 0) ? 'r' : '-',\n        ((a.type & QBDI::MEMORY_WRITE) != 0) ? 'w' : '-', a.value, a.flags);\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nstruct ExpectedMemoryAccess {\n  QBDI::rword address;\n  QBDI::rword value;\n  uint16_t size;\n  QBDI::MemoryAccessType type;\n  QBDI::MemoryAccessFlags flags;\n  bool see = false;\n};\n\nstruct ExpectedMemoryAccesses {\n  std::vector<ExpectedMemoryAccess> accesses;\n};\n\nstatic QBDI::VMAction checkAccess(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n\n  ExpectedMemoryAccesses *info = static_cast<ExpectedMemoryAccesses *>(data);\n  if (std::all_of(info->accesses.begin(), info->accesses.end(),\n                  [](ExpectedMemoryAccess &a) { return a.see; }))\n    return QBDI::VMAction::CONTINUE;\n\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n\n  CHECKED_IF(memaccesses.size() == info->accesses.size()) {\n    for (size_t i = 0; i < info->accesses.size(); i++) {\n      auto &memaccess = memaccesses[i];\n      auto &expect = info->accesses[i];\n      INFO(\"Expected Access n°\" << i);\n      INFO(\"Value 0x\" << std::hex << memaccess.value << \" expect 0x\" << std::hex\n                      << expect.value);\n      CHECKED_IF(memaccess.accessAddress == expect.address)\n      CHECKED_IF((memaccess.value == expect.value || expect.value == 0))\n      CHECKED_IF(memaccess.size == expect.size)\n      CHECKED_IF(memaccess.type == expect.type)\n      CHECKED_IF(memaccess.flags == expect.flags)\n      expect.see = true;\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\n// tLDMIA\n// ======\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tldmia1\") {\n\n  const char source[] = \"ldmia r0, {r0, r1}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == v1[0]);\n  CHECK(state->r1 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tldmia2\") {\n\n  const char source[] = \"ldmia r0, {r0-r7}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (unsigned int i = 0; i < 8; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tldmia_post1\") {\n\n  const char source[] = \"ldmia r0!, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 8);\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tldmia_post2\") {\n\n  const char source[] = \"ldmia r0!, {r1-r7}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tLDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 28);\n  for (unsigned int i = 0; i < 7; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n}\n\n// t2LDMIA\n// =======\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia1\") {\n\n  const char source[] = \"ldmia.w r0, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia2\") {\n\n  const char source[] = \"ldmia.w r0, {r0-r12,lr}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xc2708a8b, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (unsigned int i = 0; i < 13; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n  CHECK(v1[13] == state->lr);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia3\") {\n\n  const char source[] = \"ldmia.w r0, {r0-r12,pc}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xc2708a8b, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (unsigned int i = 0; i < 13; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n  CHECK(v1[13] == state->pc);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia4\") {\n\n  const char source[] = \"cmp r1, #42; it ne; ldmiane.w r0, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia_post1\") {\n\n  const char source[] = \"ldmia.w r0!, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia_post2\") {\n\n  const char source[] = \"ldmia.w r0!, {r1-r12,lr}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 4 * 13);\n  for (unsigned int i = 0; i < 12; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n  CHECK(v1[12] == state->lr);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia_post3\") {\n\n  const char source[] = \"ldmia.w r0!, {r1-r12,pc}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 4 * 13);\n  for (unsigned int i = 0; i < 12; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n  CHECK(v1[12] == state->pc);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmia_post4\") {\n\n  const char source[] = \"cmp r1, #42; it ne; ldmiane r0!, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMIA_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\n// t2LDMDB\n// =======\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb1\") {\n\n  const char source[] = \"ldmdb r0, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb2\") {\n\n  const char source[] = \"ldmdb r0, {r0-r12,lr}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 14;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (unsigned int i = 0; i < 13; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n  CHECK(v1[13] == state->lr);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb3\") {\n\n  const char source[] = \"ldmdb r0, {r0-r12,pc}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729,\n                      0x719ca3c1, 0xd7b24369, 0xb25e4516, 0x235b2fc3,\n                      0xaad33b87, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v1[13], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 14;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (unsigned int i = 0; i < 13; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n  CHECK(v1[13] == state->pc);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb4\") {\n\n  const char source[] = \"cmp r1, #42; it ne; ldmdbne r0, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb_post1\") {\n\n  const char source[] = \"ldmdb r0!, {r1, r2}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb_post2\") {\n\n  const char source[] = \"ldmdb r0!, {r1-r12,lr}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 13;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (unsigned int i = 0; i < 12; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n  CHECK(v1[12] == state->lr);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb_post3\") {\n\n  const char source[] = \"ldmdb r0!, {r1-r12,pc}\\n\";\n\n  QBDI::rword v1[] = {0x747f4b7e, 0x31eed260, 0xf21a4416, 0x319b8e1b,\n                      0x215f4510, 0x927e556e, 0xa8a0e729, 0x719ca3c1,\n                      0xd7b24369, 0xb25e4516, 0x235b2fc3, 0xc2708a8b,\n                      0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v1[9], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v1[10], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v1[11], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v1[12], 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 4 * 13;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  for (unsigned int i = 0; i < 12; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i + 1));\n  }\n  CHECK(v1[12] == state->pc);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2ldmdb_post4\") {\n\n  const char source[] = \"cmp r1, #42; it ne; ldmdbne r0!, {r1, r2, pc}\\n\";\n\n  // with condition reach\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2LDMDB_UPD\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == v1[0]);\n  CHECK(state->r2 == v1[1]);\n\n  // with condition not reach\n  expectedPre = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(not e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r1 == 42);\n}\n\n// tPop\n// ====\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tpop1\") {\n\n  const char source[] = \"mov sp, r12; pop {r0, r1}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tPOP\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r12 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  CHECK(state->sp == reinterpret_cast<QBDI::rword>(&v1) + 8);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->r0 == v1[0]);\n  CHECK(state->r1 == v1[1]);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tpop2\") {\n\n  const char source[] = \"mov sp, r12; pop {r0-r7}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260, 0xf21a4416,\n                      0x319b8e1b, 0x215f4510, 0x927e556e, 0xa8a0e729};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tPOP\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r12 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->sp == reinterpret_cast<QBDI::rword>(&v1) + 32);\n  for (unsigned int i = 0; i < 8; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tpop3\") {\n\n  const char source[] = \"mov sp, r12; pop {r0-r7,pc}\\n\";\n\n  QBDI::rword v1[] = {0x5de254a1, 0x747f4b7e, 0x31eed260,\n                      0xf21a4416, 0x319b8e1b, 0x215f4510,\n                      0x927e556e, 0xa8a0e729, 0x2a};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[0], v1[0], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v1[1], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v1[2], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v1[3], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v1[4], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v1[5], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v1[6], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v1[7], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v1[8], 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tPOP\", QBDI::PREINST, checkAccess, &expectedPre);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r12 = reinterpret_cast<QBDI::rword>(&v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  state = vm.getGPRState();\n  CHECK(state->sp == reinterpret_cast<QBDI::rword>(&v1) + 36);\n  for (unsigned int i = 0; i < 8; i++) {\n    INFO(\"Offset \" << i);\n    CHECK(v1[i] == QBDI_GPR_GET(state, i));\n  }\n  CHECK(v1[8] == state->pc);\n}\n\n// tSTMIA_UPD\n// ==========\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tstmia1\") {\n\n  const char source[] = \"stmia r0!, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tstmia2\") {\n\n  const char source[] = \"stmia r0!, {r0, r1, r2}\\n\";\n\n  QBDI::rword v[] = {0, 0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  v[0] = (QBDI::rword)&v1;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tSTMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[1];\n  state->r2 = v[2];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n}\n\n// t2STMIA\n// =======\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmia1\") {\n\n  const char source[] = \"stmia.w r0, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmia2\") {\n\n  const char source[] = \"stmia.w r0, {r0-r12, lr}\\n\";\n\n  QBDI::rword v[] = {0,          0x31eed260, 0xf21a4416, 0x319b8e1b, 0x215f4510,\n                     0x927e556e, 0xa8a0e729, 0x719ca3c1, 0xd7b24369, 0xb25e4516,\n                     0x235b2fc3, 0xc2708a8b, 0x75ef1380, 0x2a};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  v[0] = (QBDI::rword)&v1;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v[3], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v[4], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v[5], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v[6], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v[7], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v[8], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v[9], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v[10], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v[11], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v[12], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v[13], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[1];\n  state->r2 = v[2];\n  state->r3 = v[3];\n  state->r4 = v[4];\n  state->r5 = v[5];\n  state->r6 = v[6];\n  state->r7 = v[7];\n  state->r8 = v[8];\n  state->r9 = v[9];\n  state->r10 = v[10];\n  state->r11 = v[11];\n  state->r12 = v[12];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmia3\") {\n\n  const char source[] = \"cmp r4, #42; it ne; stmiane.w r0, {r1, r2, r3}\\n\";\n\n  // with condition reach\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMIA\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = v[2];\n  state->r4 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n\n  // with condition not reach\n  expectedPost = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0] + 1;\n  state->r2 = v[1] + 1;\n  state->r3 = v[2] + 1;\n  state->r4 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmia_post1\") {\n\n  const char source[] = \"stmia.w r0!, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMIA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 8);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmia_post2\") {\n\n  const char source[] = \"stmia.w r0!, {r1-r12, lr}\\n\";\n\n  QBDI::rword v[] = {0x31eed260, 0xf21a4416, 0x319b8e1b, 0x215f4510, 0x927e556e,\n                     0xa8a0e729, 0x719ca3c1, 0x75ef1380, 0xd7b24369, 0xb25e4516,\n                     0x235b2fc3, 0xc2708a8b, 0x2a};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v[3], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v[4], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v[5], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v[6], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v[7], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v[8], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v[9], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v[10], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v[11], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v[12], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMIA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = v[2];\n  state->r4 = v[3];\n  state->r5 = v[4];\n  state->r6 = v[5];\n  state->r7 = v[6];\n  state->r8 = v[7];\n  state->r9 = v[8];\n  state->r10 = v[9];\n  state->r11 = v[10];\n  state->r12 = v[11];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 52);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmia_post3\") {\n\n  const char source[] = \"cmp r4, #42; it ne; stmiane.w r0!, {r1, r2, r3}\\n\";\n\n  // with condition reach\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMIA_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = v[2];\n  state->r4 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 12);\n\n  // with condition not reach\n  expectedPost = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1);\n  state->r1 = v[0] + 1;\n  state->r2 = v[1] + 1;\n  state->r3 = v[2] + 1;\n  state->r4 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n}\n\n// t2STMDB\n// =======\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmdb1\") {\n\n  const char source[] = \"stmdb.w r0, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMDB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 8);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmdb2\") {\n\n  const char source[] = \"stmdb.w r0, {r0-r12, lr}\\n\";\n\n  QBDI::rword v[] = {0,          0x31eed260, 0xf21a4416, 0x319b8e1b, 0x215f4510,\n                     0x927e556e, 0xa8a0e729, 0x719ca3c1, 0xd7b24369, 0xb25e4516,\n                     0x235b2fc3, 0xc2708a8b, 0x75ef1380, 0x2a};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  v[0] = (QBDI::rword)&v1 + 56;\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v[3], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v[4], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v[5], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v[6], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v[7], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v[8], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v[9], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v[10], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v[11], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v[12], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[13], v[13], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMDB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 56;\n  state->r1 = v[1];\n  state->r2 = v[2];\n  state->r3 = v[3];\n  state->r4 = v[4];\n  state->r5 = v[5];\n  state->r6 = v[6];\n  state->r7 = v[7];\n  state->r8 = v[8];\n  state->r9 = v[9];\n  state->r10 = v[10];\n  state->r11 = v[11];\n  state->r12 = v[12];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 56);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmdb3\") {\n\n  const char source[] = \"cmp r4, #42; it ne; stmdbne.w r0, {r1, r2, r3}\\n\";\n\n  // with condition reach\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMDB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = v[2];\n  state->r4 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n\n  // with condition not reach\n  expectedPost = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0] + 1;\n  state->r2 = v[1] + 1;\n  state->r3 = v[2] + 1;\n  state->r4 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 12);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmdb_post1\") {\n\n  const char source[] = \"stmdb.w r0!, {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMDB_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmdb_post2\") {\n\n  const char source[] = \"stmdb.w r0!, {r1-r12, lr}\\n\";\n\n  QBDI::rword v[] = {0x31eed260, 0xf21a4416, 0x319b8e1b, 0x215f4510, 0x927e556e,\n                     0xa8a0e729, 0x719ca3c1, 0x75ef1380, 0xd7b24369, 0xb25e4516,\n                     0x235b2fc3, 0xc2708a8b, 0x2a};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v[3], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v[4], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v[5], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v[6], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v[7], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v[8], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[9], v[9], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[10], v[10], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[11], v[11], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[12], v[12], 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMDB_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 52;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = v[2];\n  state->r4 = v[3];\n  state->r5 = v[4];\n  state->r6 = v[5];\n  state->r7 = v[6];\n  state->r8 = v[7];\n  state->r9 = v[8];\n  state->r10 = v[9];\n  state->r11 = v[10];\n  state->r12 = v[11];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-t2stmdb_post3\") {\n\n  const char source[] = \"cmp r4, #42; it ne; stmdbne.w r0!, {r1, r2, r3}\\n\";\n\n  // with condition reach\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"t2STMDB_UPD\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = v[2];\n  state->r4 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1));\n\n  // with condition not reach\n  expectedPost = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0] + 1;\n  state->r2 = v[1] + 1;\n  state->r3 = v[2] + 1;\n  state->r4 = 42;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->r0 == reinterpret_cast<QBDI::rword>(&v1) + 12);\n}\n\n// tPUSH\n// =====\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tpush1\") {\n\n  const char source[] = \"mov sp, r0; push {r1, r2}\\n\";\n\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tPUSH\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 8;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->sp == reinterpret_cast<QBDI::rword>(&v1));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tpush2\") {\n\n  const char source[] = \"mov sp, r8; push {r0-r7, lr}\\n\";\n\n  QBDI::rword v[] = {0x31eed260, 0xf21a4416, 0x319b8e1b, 0x215f4510, 0xd7b24369,\n                     0xb25e4516, 0x235b2fc3, 0xc2708a8b, 0x2a};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[3], v[3], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[4], v[4], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[5], v[5], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[6], v[6], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[7], v[7], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[8], v[8], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tPUSH\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->r0 = v[0];\n  state->r1 = v[1];\n  state->r2 = v[2];\n  state->r3 = v[3];\n  state->r4 = v[4];\n  state->r5 = v[5];\n  state->r6 = v[6];\n  state->r7 = v[7];\n  state->r8 = reinterpret_cast<QBDI::rword>(&v1) + 36;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->sp == reinterpret_cast<QBDI::rword>(&v1));\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_Thumb-tpush3\") {\n\n  const char source[] = \"cmp r4, #42; mov sp, r0; it ne; pushne {r1, r2, r3}\\n\";\n\n  // with condition reach\n  QBDI::rword v[] = {0x5de254a1, 0x747f4b7e, 0xd7b24369};\n  QBDI::rword v1[sizeof(v) / sizeof(QBDI::rword)] = {0};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1[0], v[0], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[1], v[1], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1[2], v[2], 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"tPUSH\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  QBDI::rword backupSP = state->sp;\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0];\n  state->r2 = v[1];\n  state->r3 = v[2];\n  state->r4 = 0;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->sp == reinterpret_cast<QBDI::rword>(&v1));\n\n  // with condition not reach\n  expectedPost = {{}};\n  state->r0 = reinterpret_cast<QBDI::rword>(&v1) + 12;\n  state->r1 = v[0] + 1;\n  state->r2 = v[1] + 1;\n  state->r3 = v[2] + 1;\n  state->r4 = 42;\n  state->sp = backupSP;\n  vm.setGPRState(state);\n\n  ran = runOnASM(&retval, source, {}, QBDI::CPUMode::Thumb);\n\n  CHECK(ran);\n  CHECK(memcmp(v, v1, sizeof(v)) == 0);\n  CHECK(state->sp == reinterpret_cast<QBDI::rword>(&v1) + 12);\n}\n"
  },
  {
    "path": "test/API/ARM/VMTest_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"VMTest_ARM.h\"\n\n#define MNEM_IMM_SHORT_VAL 66\n#define MNEM_IMM_VAL 42\n#define MNEM_IMM_SHORT_STRVAL \"66\"\n#define MNEM_IMM_STRVAL \"42\"\n\nconst struct TestInst TestInsts[MNEM_COUNT] = {\n    {4,\n     2,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 3, \"R3\",\n          QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, MNEM_IMM_SHORT_VAL, 4, 0,\n          -1, nullptr, QBDI::REGISTER_UNUSED},\n     }}};\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0) {\n  QBDI::rword volatile res = arg0 + 0x666;\n  asm(\"cmp r3, #\" MNEM_IMM_SHORT_STRVAL);\n  return res;\n}\n\n// clang-format off\nstd::vector<uint8_t> VMTest_ARM_InvalidInstruction = {\n  0x64, 0x00, 0xa0, 0xe3,     // mov      r0, #0x64\n  0x01, 0x10, 0x21, 0xe0,     // eor      r1, r1, r1\n  0x00, 0x10, 0x01, 0xe0,     // add      r1, r1, r0\n  0x01 ,0x00, 0x40, 0xe2,     // sub      r0, r0, #1\n  0x00, 0x00, 0x50, 0xe3,     // cmp      r0, #0\n  0xff, 0xff, 0xff, 0xff,     // invalid instruction\n  0xaa, 0xab                  // unaligned instruction\n};\n\nstd::vector<uint8_t> VMTest_ARM_BreakingInstruction = {\n  0x64, 0x00, 0xa0, 0xe3,     // mov      r0, #0x64\n  0x01, 0x10, 0x21, 0xe0,     // eor      r1, r1, r1\n  0x00, 0x10, 0x01, 0xe0,     // add      r1, r1, r0\n  0x01 ,0x00, 0x40, 0xe2,     // sub      r0, r0, #1\n  0x00, 0x00, 0x50, 0xe3,     // cmp      r0, #0\n  0x1e, 0xff, 0x2f, 0xe1      // bx       lr\n};\n\nstd::vector<uint8_t> VMTest_ARM_SelfModifyingCode1 = {\n  0x00, 0x00, 0xa0, 0xe3,     // mov  r0, #0x0\n  0x00, 0x00, 0x0f, 0xe5,     // str  r0, [pc, #0]\n  0x2a, 0x00, 0xa0, 0xe3,     // mov  r0, #0x2a\n  0xff, 0xff, 0xff, 0xff,     // invalid instruction, replaced by 'andeq r0, r0, r0'\n  0x1e, 0xff, 0x2f, 0xe1      // bx   lr\n};\n\nstd::vector<uint8_t> VMTest_ARM_SelfModifyingCode2 = {\n  0x00, 0x00, 0xa0, 0xe3,     // mov  r0, #0x0\n  0x00, 0x00, 0x0f, 0xe5,     // str  r0, [pc, #0]\n  0x2a, 0x00, 0xa0, 0xe3,     // mov  r0, #0x2a\n  0x01, 0x0c, 0x80, 0xe2,     // add  r0, r0, #256, replaced by 'andeq r0, r0, r0'\n  0x1e, 0xff, 0x2f, 0xe1      // bx   lr\n};\n// clang-format on\n\nstd::unordered_map<std::string, SizedTestCode> TestCode = {\n    {\"VMTest-InvalidInstruction\", {VMTest_ARM_InvalidInstruction, 0x10}},\n    {\"VMTest-BreakingInstruction\", {VMTest_ARM_BreakingInstruction, 0x10}},\n    {\"VMTest-SelfModifyingCode1\", {VMTest_ARM_SelfModifyingCode1}},\n    {\"VMTest-SelfModifyingCode2\", {VMTest_ARM_SelfModifyingCode2}}};\n"
  },
  {
    "path": "test/API/ARM/VMTest_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDITEST_VMTEST_ARM_H\n#define QBDITEST_VMTEST_ARM_H\n\n#include <unordered_map>\n#include <vector>\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/VM.h\"\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0);\n\n#define MNEM_COUNT 1u\n#define MNEM_VALIDATION 20u\n\n#define MAX_OPERAND 4\n#define MNEM_CMP \"CMP*\"\n\nstruct SizedTestCode {\n  std::vector<uint8_t> code;\n  std::size_t size;\n};\n\nstruct TestInst {\n  uint32_t instSize;\n  uint8_t numOperands;\n  bool isCompare;\n  QBDI::RegisterAccessType flagsAccess;\n  QBDI::OperandAnalysis operands[MAX_OPERAND];\n};\n\nextern const struct TestInst TestInsts[MNEM_COUNT];\nextern std::unordered_map<std::string, SizedTestCode> TestCode;\n\n#define SKIPTESTASM \"nop\\nnop\\nbx lr\\n\"\n\n#endif\n"
  },
  {
    "path": "test/API/AllocTest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <cstdint>\n#include <stdio.h>\n\n#include <catch2/catch_test_macros.hpp>\n#include \"QBDI/Memory.hpp\"\n\nTEST_CASE(\"AllocAlignedTest-CorrectSize\") {\n  const static size_t size = 1000;\n  uint8_t *array = (uint8_t *)QBDI::alignedAlloc(size, sizeof(void *));\n  REQUIRE(array != nullptr);\n  for (size_t i = 0; i < size; i++) {\n    array[i] = 0x42;\n  }\n  QBDI::alignedFree(array);\n  SUCCEED();\n}\n\nTEST_CASE(\"AllocAlignedTest-CorrectAlignement\") {\n  const static size_t size = 1000;\n  const static size_t align = 16;\n  void *array = QBDI::alignedAlloc(size, align);\n  REQUIRE(array != nullptr);\n  REQUIRE(reinterpret_cast<std::uintptr_t>(array) % align ==\n          static_cast<std::uintptr_t>(0));\n  QBDI::alignedFree(array);\n  SUCCEED();\n}\n"
  },
  {
    "path": "test/API/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/AllocTest.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/APITest.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/RangeTest.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/VMTest.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest.cpp\")\n\nif(QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "test/API/MemoryAccessTest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"APITest.h\"\n\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#define FAKE_RET_ADDR 0x666\n\n#define N_SUM(N) ((N) * ((N) + 1) / 2)\n#define OFFSET_SUM(N) N_SUM((N) - 1)\n\nstruct TestInfo {\n  void *buffer;\n  size_t buffer_size;\n  size_t i;\n};\n\nQBDI::rword arrayRead8(volatile char *buffer, size_t size) {\n  size_t i = 0, sum = 0;\n  for (i = 0; i < size; i++) {\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword arrayRead16(volatile uint16_t *buffer, size_t size) {\n  size_t i = 0, sum = 0;\n  for (i = 0; i < size; i++) {\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword arrayRead32(volatile uint32_t *buffer, size_t size) {\n  size_t i = 0, sum = 0;\n  for (i = 0; i < size; i++) {\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword arrayWrite8(volatile uint8_t *buffer, size_t size) {\n  size_t i = 0, sum = 0;\n  buffer[0] = 0;\n  for (i = 1; i < size; i++) {\n    buffer[i] = buffer[i - 1] + i;\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword arrayWrite16(volatile uint16_t *buffer, size_t size) {\n  size_t i = 0, sum = 0;\n  buffer[0] = 0;\n  for (i = 1; i < size; i++) {\n    buffer[i] = buffer[i - 1] + i;\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword arrayWrite32(volatile uint32_t *buffer, size_t size) {\n  size_t i = 0, sum = 0;\n  buffer[0] = 0;\n  for (i = 1; i < size; i++) {\n    buffer[i] = buffer[i - 1] + i;\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword unrolledRead(volatile char *buffer) {\n  size_t sum = buffer[0];\n  sum += buffer[1];\n  sum += buffer[2];\n  sum += buffer[3];\n  sum += buffer[4];\n  sum += buffer[5];\n  sum += buffer[6];\n  sum += buffer[7];\n  sum += buffer[8];\n  sum += buffer[9];\n  sum += buffer[10];\n  return sum;\n}\n\nQBDI::rword unrolledWrite(volatile char *buffer) {\n  size_t sum = 1;\n  buffer[0] = sum;\n  sum += buffer[0];\n  buffer[1] = sum;\n  sum += buffer[1];\n  buffer[2] = sum;\n  sum += buffer[2];\n  buffer[3] = sum;\n  sum += buffer[3];\n  buffer[4] = sum;\n  sum += buffer[4];\n  buffer[5] = sum;\n  sum += buffer[5];\n  buffer[6] = sum;\n  sum += buffer[6];\n  buffer[7] = sum;\n  sum += buffer[7];\n  buffer[8] = sum;\n  sum += buffer[8];\n  buffer[9] = sum;\n  sum += buffer[9];\n  buffer[10] = sum;\n  sum += buffer[10];\n  return sum;\n}\n\nQBDI::rword unrolledReadLoop(volatile char *buffer, size_t len) {\n  size_t sum = 0, i = 0;\n  for (i = 0; i < len; i++) {\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword unrolledWriteLoop(volatile char *buffer, size_t len) {\n  size_t sum = 1, i = 0;\n  for (i = 0; i < len; i++) {\n    buffer[i] = sum;\n    sum += buffer[i];\n  }\n  return sum;\n}\n\nQBDI::rword mad(volatile uint32_t *a, volatile uint32_t *b,\n                volatile uint32_t *c) {\n  uint32_t av = *a;\n  uint32_t bv = *b;\n  uint32_t cv = *c;\n  cv = (av + cv) * (bv + cv);\n  *c = cv;\n  return *c;\n}\n\nQBDI::VMAction checkArrayRead8(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                               QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_READ) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = memaccess.accessAddress - brange.start();\n        if ((QBDI::rword)((uint8_t *)info->buffer)[offset] == memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkArrayRead16(QBDI::VMInstanceRef vm,\n                                QBDI::GPRState *gprState,\n                                QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_READ) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = (memaccess.accessAddress - brange.start()) >> 1;\n        if ((QBDI::rword)((uint16_t *)info->buffer)[offset] ==\n            memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkArrayRead32(QBDI::VMInstanceRef vm,\n                                QBDI::GPRState *gprState,\n                                QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_READ) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = (memaccess.accessAddress - brange.start()) >> 2;\n        if ((QBDI::rword)((uint32_t *)info->buffer)[offset] ==\n            memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkArrayWrite8(QBDI::VMInstanceRef vm,\n                                QBDI::GPRState *gprState,\n                                QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_WRITE) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = memaccess.accessAddress - brange.start();\n        if ((QBDI::rword)((uint8_t *)info->buffer)[offset] == memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkArrayWrite16(QBDI::VMInstanceRef vm,\n                                 QBDI::GPRState *gprState,\n                                 QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_WRITE) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = (memaccess.accessAddress - brange.start()) >> 1;\n        if ((QBDI::rword)((uint16_t *)info->buffer)[offset] ==\n            memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkArrayWrite32(QBDI::VMInstanceRef vm,\n                                 QBDI::GPRState *gprState,\n                                 QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_WRITE) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = (memaccess.accessAddress - brange.start()) >> 2;\n        if ((QBDI::rword)((uint32_t *)info->buffer)[offset] ==\n            memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkUnrolledReadInst(QBDI::VMInstanceRef vm,\n                                     QBDI::GPRState *gprState,\n                                     QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_READ) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = memaccess.accessAddress - brange.start();\n        if ((QBDI::rword)((uint8_t *)info->buffer)[offset] == memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkUnrolledWriteInst(QBDI::VMInstanceRef vm,\n                                      QBDI::GPRState *gprState,\n                                      QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_WRITE) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = memaccess.accessAddress - brange.start();\n        if ((QBDI::rword)((uint8_t *)info->buffer)[offset] == memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkUnrolledReadBB(QBDI::VMInstanceRef vm,\n                                   const QBDI::VMState *vmState,\n                                   QBDI::GPRState *gprState,\n                                   QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getBBMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_READ) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = memaccess.accessAddress - brange.start();\n        if ((QBDI::rword)((uint8_t *)info->buffer)[offset] == memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction checkUnrolledWriteBB(QBDI::VMInstanceRef vm,\n                                    const QBDI::VMState *vmState,\n                                    QBDI::GPRState *gprState,\n                                    QBDI::FPRState *fprState, void *data) {\n\n  TestInfo *info = (TestInfo *)data;\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getBBMemoryAccess();\n  QBDI::Range<QBDI::rword> brange(\n      (QBDI::rword)info->buffer,\n      ((QBDI::rword)info->buffer) + info->buffer_size, QBDI::auth_addr_t());\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_WRITE) {\n      if (brange.contains(memaccess.accessAddress)) {\n        size_t offset = memaccess.accessAddress - brange.start();\n        if ((QBDI::rword)((uint8_t *)info->buffer)[offset] == memaccess.value) {\n          info->i += offset;\n        }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction readSnooper(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                           QBDI::FPRState *fprState, void *data) {\n\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_READ) {\n      // If read from data\n      if (memaccess.accessAddress == (QBDI::rword)data) {\n        // replace data\n        *((uint32_t *)data) = 0x42;\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction writeSnooper(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                            QBDI::FPRState *fprState, void *data) {\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  for (const QBDI::MemoryAccess &memaccess : memaccesses) {\n    if (memaccess.type == QBDI::MEMORY_WRITE) {\n      // If write to data\n      if (memaccess.accessAddress == (QBDI::rword)data) {\n        // replace data\n        *((uint32_t *)data) = 0x42;\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-Read8\") {\n  char buffer[] = \"p0p30fd0p3\";\n  size_t buffer_size = sizeof(buffer) / sizeof(char);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemAccessCB(QBDI::MEMORY_READ, checkArrayRead8, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayRead8, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayRead8(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-Read16\") {\n  uint16_t buffer[] = {44595, 59483, 57377, 31661, 846,\n                       56570, 46925, 62955, 25481, 41095};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint16_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemAccessCB(QBDI::MEMORY_READ, checkArrayRead16, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayRead16, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayRead16(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-Read32\") {\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemAccessCB(QBDI::MEMORY_READ, checkArrayRead32, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayRead32, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-Write8\") {\n  const size_t buffer_size = 10;\n  uint8_t buffer[buffer_size];\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemAccessCB(QBDI::MEMORY_WRITE, checkArrayWrite8, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayWrite8, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayWrite8(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-Write16\") {\n  const size_t buffer_size = 10;\n  uint16_t buffer[buffer_size];\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemAccessCB(QBDI::MEMORY_WRITE, checkArrayWrite16, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayWrite16, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayWrite16(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-Write32\") {\n  const size_t buffer_size = 10;\n  uint32_t buffer[buffer_size];\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemAccessCB(QBDI::MEMORY_WRITE, checkArrayWrite32, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayWrite32, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayWrite32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-BasicBlockRead\") {\n  char buffer[] = \"p0p30fd0p3\";\n  size_t buffer_size = sizeof(buffer) / sizeof(char);\n  TestInfo infoInst = {(void *)buffer, sizeof(buffer), 0};\n  TestInfo infoBB = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addVMEventCB(QBDI::VMEvent::SEQUENCE_EXIT, checkUnrolledReadBB, &infoBB);\n  vm.addMemAccessCB(QBDI::MEMORY_READ, checkUnrolledReadInst, &infoInst);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {(QBDI::rword)buffer});\n  bool ran = vm.run((QBDI::rword)unrolledRead, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)unrolledRead(buffer));\n  REQUIRE(OFFSET_SUM(buffer_size) == infoBB.i);\n  REQUIRE(infoInst.i == infoBB.i);\n\n  infoInst.i = 0;\n  infoBB.i = 0;\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {(QBDI::rword)buffer, buffer_size});\n  ran = vm.run((QBDI::rword)unrolledReadLoop, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)unrolledReadLoop(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == infoBB.i);\n  REQUIRE(infoInst.i == infoBB.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-BasicBlockWrite\") {\n  const size_t buffer_size = 11;\n  char buffer[buffer_size];\n  TestInfo infoInst = {(void *)buffer, sizeof(buffer), 0};\n  TestInfo infoBB = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_WRITE);\n  vm.addVMEventCB(QBDI::VMEvent::SEQUENCE_EXIT, checkUnrolledWriteBB, &infoBB);\n  vm.addMemAccessCB(QBDI::MEMORY_WRITE, checkUnrolledWriteInst, &infoInst);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {(QBDI::rword)buffer});\n  bool ran = vm.run((QBDI::rword)unrolledWrite, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)unrolledWrite(buffer));\n  REQUIRE(OFFSET_SUM(buffer_size) == infoBB.i);\n  REQUIRE(infoInst.i == infoBB.i);\n\n  infoInst.i = 0;\n  infoBB.i = 0;\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {(QBDI::rword)buffer, buffer_size});\n  ran = vm.run((QBDI::rword)unrolledWriteLoop, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)unrolledWriteLoop(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == infoBB.i);\n  REQUIRE(infoInst.i == infoBB.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-ReadRange\") {\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                   QBDI::MEMORY_READ, checkArrayRead32, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayRead32, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-WriteRange\") {\n  const size_t buffer_size = 10;\n  uint32_t buffer[buffer_size];\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                   QBDI::MEMORY_WRITE, checkArrayWrite32, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayWrite32, (QBDI::rword)FAKE_RET_ADDR);\n\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayWrite32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-ReadWriteRange\") {\n  const size_t buffer_size = 10;\n  uint32_t buffer[buffer_size];\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  // Array write\n  uint32_t cb1 =\n      vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                       QBDI::MEMORY_READ_WRITE, checkArrayWrite32, &info);\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  bool ran = vm.run((QBDI::rword)arrayWrite32, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(true == ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayWrite32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n\n  // Array read\n  info.i = 0;\n  vm.deleteInstrumentation(cb1);\n  vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                   QBDI::MEMORY_READ_WRITE, checkArrayRead32, &info);\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ran = vm.run((QBDI::rword)arrayRead32, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(true == ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-MemorySnooping\") {\n  uint32_t a = 10, b = 42, c = 1337;\n  QBDI::rword original = mad(&a, &b, &c);\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n\n  // Will replace a with 0x42 on read,  Will replace c with 0x42 on write\n  uint32_t snoop1 =\n      vm.addMemAddrCB((QBDI::rword)&a, QBDI::MEMORY_READ, readSnooper, &a);\n  uint32_t snoop2 =\n      vm.addMemAddrCB((QBDI::rword)&c, QBDI::MEMORY_WRITE, writeSnooper, &c);\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)&a, (QBDI::rword)&b, (QBDI::rword)&c});\n  a = 10, b = 42, c = 1337;\n  vm.run((QBDI::rword)mad, (QBDI::rword)FAKE_RET_ADDR);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE((QBDI::rword)0x42 == ret);\n  // Will replace b with 0x42 on read, no effect because snoop2 is still active\n  uint32_t snoop3 =\n      vm.addMemAddrCB((QBDI::rword)&b, QBDI::MEMORY_READ, readSnooper, &b);\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)&a, (QBDI::rword)&b, (QBDI::rword)&c});\n  a = 10, b = 42, c = 1337;\n  vm.run((QBDI::rword)mad, (QBDI::rword)FAKE_RET_ADDR);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE((QBDI::rword)0x42 == ret);\n  // Deleting snoop2, effect of snoop1 and snoop3\n  vm.deleteInstrumentation(snoop2);\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)&a, (QBDI::rword)&b, (QBDI::rword)&c});\n  a = 10, b = 42, c = 1337;\n  vm.run((QBDI::rword)mad, (QBDI::rword)FAKE_RET_ADDR);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  a = 0x42, b = 0x42, c = 1337;\n  REQUIRE(mad(&a, &b, &c) == ret);\n  // Deleting snoop1, effect of snoop3\n  vm.deleteInstrumentation(snoop1);\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)&a, (QBDI::rword)&b, (QBDI::rword)&c});\n  a = 10, b = 42, c = 1337;\n  vm.run((QBDI::rword)mad, (QBDI::rword)FAKE_RET_ADDR);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  a = 10, b = 0x42, c = 1337;\n  REQUIRE(mad(&a, &b, &c) == ret);\n  // Deleting snoop3\n  vm.deleteInstrumentation(snoop3);\n  QBDI::simulateCall(state, FAKE_RET_ADDR,\n                     {(QBDI::rword)&a, (QBDI::rword)&b, (QBDI::rword)&c});\n  a = 10, b = 42, c = 1337;\n  vm.run((QBDI::rword)mad, (QBDI::rword)FAKE_RET_ADDR);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(original == ret);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-VMCpy1\") {\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  // copy constructor\n  QBDI::VM vm2 = vm;\n\n  // copy operator\n  QBDI::VM vm3;\n  vm3.precacheBasicBlock((QBDI::rword)arrayRead32);\n  vm3 = vm;\n\n  vm2.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                    QBDI::MEMORY_READ, checkArrayRead32, &info);\n  vm3.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                    QBDI::MEMORY_READ, checkArrayRead32, &info);\n\n  vm.deleteAllInstrumentations();\n\n  QBDI::rword retval;\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 0);\n\n  vm2.call(&retval, (QBDI::rword)arrayRead32,\n           {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n\n  info.i = 0;\n  vm3.call(&retval, (QBDI::rword)arrayRead32,\n           {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-VMCpy2\") {\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                   QBDI::MEMORY_READ, checkArrayRead32, &info);\n\n  // copy constructor\n  QBDI::VM vm2 = vm;\n\n  // copy operator\n  QBDI::VM vm3;\n  vm3.precacheBasicBlock((QBDI::rword)arrayRead32);\n  vm3 = vm;\n\n  vm.deleteAllInstrumentations();\n\n  QBDI::rword retval;\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 0);\n\n  vm2.call(&retval, (QBDI::rword)arrayRead32,\n           {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n\n  info.i = 0;\n  vm3.call(&retval, (QBDI::rword)arrayRead32,\n           {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-InstCbLambda-VMCpy\") {\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n\n  vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n                   QBDI::MEMORY_READ,\n                   [&info](QBDI::VMInstanceRef vm, QBDI::GPRState *gpr,\n                           QBDI::FPRState *fpr) {\n                     return checkArrayRead32(vm, gpr, fpr, &info);\n                   });\n\n  // copy constructor\n  QBDI::VM vm2 = vm;\n\n  // copy operator\n  QBDI::VM vm3;\n  vm3.precacheBasicBlock((QBDI::rword)arrayRead32);\n  vm3 = vm;\n\n  vm.deleteAllInstrumentations();\n\n  QBDI::rword retval;\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 0);\n\n  vm2.call(&retval, (QBDI::rword)arrayRead32,\n           {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n\n  info.i = 0;\n  vm3.call(&retval, (QBDI::rword)arrayRead32,\n           {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-InstCbLambda-addMemAccessCB\") {\n  QBDI::rword retval;\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n  bool cbCalled = false;\n\n  QBDI::InstCbLambda cbk = [&info, &cbCalled](QBDI::VMInstanceRef vm,\n                                              QBDI::GPRState *gpr,\n                                              QBDI::FPRState *fpr) {\n    cbCalled = true;\n    return checkArrayRead32(vm, gpr, fpr, &info);\n  };\n  vm.addMemAccessCB(QBDI::MEMORY_READ, cbk);\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  info.i = 0;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 0);\n  REQUIRE(!cbCalled);\n\n  vm.addMemAccessCB(QBDI::MEMORY_READ, std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-InstCbLambda-addMemAddrCB\") {\n  QBDI::rword retval;\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n  bool cbCalled = false;\n\n  QBDI::InstCbLambda cbk = [&info, &cbCalled](QBDI::VMInstanceRef vm,\n                                              QBDI::GPRState *gpr,\n                                              QBDI::FPRState *fpr) {\n    cbCalled = true;\n    return checkArrayRead32(vm, gpr, fpr, &info);\n  };\n  vm.addMemAddrCB((QBDI::rword)buffer + 12, QBDI::MEMORY_READ, cbk);\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 3);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  info.i = 0;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 0);\n  REQUIRE(!cbCalled);\n\n  vm.addMemAddrCB((QBDI::rword)buffer + 16, QBDI::MEMORY_READ, std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 4);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-InstCbLambda-addMemRangeCB\") {\n  QBDI::rword retval;\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  TestInfo info = {(void *)buffer, sizeof(buffer), 0};\n  bool cbCalled = false;\n\n  QBDI::InstCbLambda cbk = [&info, &cbCalled](QBDI::VMInstanceRef vm,\n                                              QBDI::GPRState *gpr,\n                                              QBDI::FPRState *fpr) {\n    cbCalled = true;\n    return checkArrayRead32(vm, gpr, fpr, &info);\n  };\n  vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)buffer + sizeof(buffer),\n                   QBDI::MEMORY_READ, cbk);\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  info.i = 0;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(info.i == 0);\n  REQUIRE(!cbCalled);\n\n  vm.addMemRangeCB((QBDI::rword)buffer, (QBDI::rword)buffer + sizeof(buffer),\n                   QBDI::MEMORY_READ, std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(OFFSET_SUM(buffer_size) == info.i);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest-NoValue\") {\n  uint32_t buffer[] = {3531902336, 1974345459, 1037124602, 2572792182,\n                       3451121073, 4105092976, 2050515100, 2786945221,\n                       1496976643, 515521533};\n  size_t buffer_size = sizeof(buffer) / sizeof(uint32_t);\n  bool noValue = true;\n\n  vm.setOptions(vm.getOptions() |\n                QBDI::Options::OPT_DISABLE_MEMORYACCESS_VALUE);\n  vm.addMemRangeCB(\n      (QBDI::rword)buffer, (QBDI::rword)(buffer + buffer_size),\n      QBDI::MEMORY_READ,\n      [&noValue](QBDI::VMInstanceRef vm, QBDI::GPRState *gpr,\n                 QBDI::FPRState *fpr) {\n        for (const auto &m : vm->getInstMemoryAccess()) {\n          if ((m.flags & QBDI::MemoryAccessFlags::MEMORY_UNKNOWN_VALUE) == 0) {\n            noValue = false;\n          }\n        }\n        return QBDI::VMAction::CONTINUE;\n      });\n\n  QBDI::rword retval;\n  vm.call(&retval, (QBDI::rword)arrayRead32,\n          {(QBDI::rword)buffer, (QBDI::rword)buffer_size});\n  ;\n  REQUIRE(retval == (QBDI::rword)arrayRead32(buffer, buffer_size));\n  REQUIRE(noValue);\n}\n"
  },
  {
    "path": "test/API/OptionsTest.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDITEST_OPTIONSTEST\n#define QBDITEST_OPTIONSTEST\n\n#include <memory>\n#include \"TestSetup/InMemoryAssembler.h\"\n#include \"QBDI/VM.h\"\n\nclass OptionsTest {\nprotected:\n  OptionsTest() : vm() {}\n\n  QBDI::VM vm;\n};\n\n#endif /* QBDITEST_OPTIONSTEST */\n"
  },
  {
    "path": "test/API/RangeTest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <stdlib.h>\n#include <vector>\n\n#include \"QBDI/Range.h\"\n#include \"Patch/Utils.h\"\n\ntemplate <typename T>\nvoid checkRangeSetInvariant(const QBDI::RangeSet<T> &set) {\n  const std::vector<QBDI::Range<T>> &ranges = set.getRanges();\n\n  for (const auto &r : ranges) {\n    // no empty ranges in the set\n    REQUIRE(r.start() < r.end());\n  }\n\n  for (unsigned i = 1; i < ranges.size(); i++) {\n    // the range in the set are sorted.\n    // Moreever, the set should be minimized\n    REQUIRE(ranges[i - 1].end() < ranges[i].start());\n  }\n}\n\nTEST_CASE(\"Range-UnitTest\") {\n\n  std::vector<QBDI::Range<int>> testRanges = {\n      {50, 60, QBDI::real_addr_t()},  {85, 90, QBDI::real_addr_t()},\n      {10, 20, QBDI::real_addr_t()},  {25, 30, QBDI::real_addr_t()},\n      {65, 70, QBDI::real_addr_t()},  {70, 75, QBDI::real_addr_t()},\n      {74, 80, QBDI::real_addr_t()},  {80, 84, QBDI::real_addr_t()},\n      {60, 85, QBDI::real_addr_t()},  {40, 60, QBDI::real_addr_t()},\n      {9, 120, QBDI::real_addr_t()},  {55, 67, QBDI::real_addr_t()},\n      {80, 200, QBDI::real_addr_t()}, {5, 150, QBDI::real_addr_t()},\n  };\n  QBDI::RangeSet<int> rangeSet;\n  QBDI::RangeSet<int> rangeSetInv;\n  rangeSetInv.add({0, 1000, QBDI::real_addr_t()});\n\n  for (unsigned i = 0; i < testRanges.size(); i++) {\n    rangeSet.add(testRanges[i]);\n    rangeSetInv.remove(testRanges[i]);\n\n    checkRangeSetInvariant(rangeSet);\n    checkRangeSetInvariant(rangeSetInv);\n\n    for (unsigned j = 0; j <= i; j++) {\n      REQUIRE(rangeSet.contains(testRanges[i]));\n      REQUIRE_FALSE(rangeSetInv.contains(testRanges[i]));\n      REQUIRE_FALSE(rangeSetInv.overlaps(testRanges[i]));\n    }\n\n    for (const auto &r : rangeSet.getRanges()) {\n      REQUIRE_FALSE(rangeSetInv.contains(r));\n      REQUIRE_FALSE(rangeSetInv.overlaps(r));\n    }\n\n    for (const auto &r : rangeSetInv.getRanges()) {\n      REQUIRE_FALSE(rangeSet.contains(r));\n      REQUIRE_FALSE(rangeSet.overlaps(r));\n    }\n  }\n}\n\nTEST_CASE(\"Range-StateIntegrity\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  static const unsigned N = 100;\n  std::vector<QBDI::Range<int>> testRanges;\n  QBDI::RangeSet<int> rangeSet;\n\n  for (unsigned i = 0; i < N; i++) {\n    int start = get_random() % 900;\n    int end = start + get_random() % 100 + 1;\n    int delta = -rangeSet.size();\n    QBDI::Range<int> r(start, end, QBDI::real_addr_t());\n\n    testRanges.push_back(r);\n    rangeSet.add(r);\n    delta += rangeSet.size();\n\n    REQUIRE(r.size() >= delta);\n    REQUIRE(rangeSet.contains(r));\n    REQUIRE(rangeSet.contains(r.start()));\n    REQUIRE(rangeSet.contains(r.end() - 1));\n    checkRangeSetInvariant(rangeSet);\n  }\n\n  for (unsigned i = 0; i < N; i++) {\n    int delta = rangeSet.size();\n    QBDI::Range<int> r = testRanges.back();\n\n    testRanges.pop_back();\n    rangeSet.remove(r);\n    delta -= rangeSet.size();\n\n    REQUIRE(r.size() >= delta);\n    REQUIRE_FALSE(rangeSet.contains(r));\n    REQUIRE_FALSE(rangeSet.contains(r.start()));\n    REQUIRE_FALSE(rangeSet.contains(r.end() - 1));\n    checkRangeSetInvariant(rangeSet);\n  }\n  REQUIRE(0 == rangeSet.size());\n}\n\ntemplate <typename T>\nvoid randomPermutation(std::vector<T> &v) {\n  size_t i = 0;\n  for (i = 0; i < v.size(); i++) {\n    size_t p = get_random() % v.size();\n    T t = v[i];\n    v[i] = v[p];\n    v[p] = t;\n  }\n}\n\nTEST_CASE(\"Range-Commutativity\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  static const unsigned N = 100;\n  std::vector<QBDI::Range<int>> testRanges;\n  QBDI::RangeSet<int> rangeSet;\n\n  for (unsigned i = 0; i < N; i++) {\n    int start = get_random() % 1000000;\n    int end = start + 1 + get_random() % 10000;\n    QBDI::Range<int> r(start, end, QBDI::real_addr_t());\n    testRanges.push_back(r);\n    rangeSet.add(r);\n    checkRangeSetInvariant(rangeSet);\n  }\n\n  for (unsigned c = 0; c < N; c++) {\n    QBDI::RangeSet<int> permutedRangeSet;\n\n    randomPermutation(testRanges);\n    for (unsigned i = 0; i < N; i++) {\n      permutedRangeSet.add(testRanges[i]);\n    }\n    REQUIRE(rangeSet.size() == permutedRangeSet.size());\n    for (size_t i = 0; i < rangeSet.getRanges().size(); i++) {\n      REQUIRE(rangeSet.getRanges()[i].start() ==\n              permutedRangeSet.getRanges()[i].start());\n      REQUIRE(rangeSet.getRanges()[i].end() ==\n              permutedRangeSet.getRanges()[i].end());\n    }\n  }\n}\n\nTEST_CASE(\"Range-Intersection\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  static const unsigned N = 100;\n  QBDI::RangeSet<int> rangeSet1;\n  QBDI::RangeSet<int> rangeSet2;\n  QBDI::RangeSet<int> intersection1;\n  QBDI::RangeSet<int> intersection2;\n\n  for (unsigned i = 0; i < N; i++) {\n    int start = get_random() % 1000000;\n    int end = start + 1 + get_random() % 10000;\n    rangeSet1.add(QBDI::Range<int>(start, end, QBDI::real_addr_t()));\n    checkRangeSetInvariant(rangeSet1);\n  }\n\n  for (unsigned i = 0; i < N; i++) {\n    int start = get_random() % 1000000;\n    int end = start + 1 + get_random() % 10000;\n    rangeSet2.add(QBDI::Range<int>(start, end, QBDI::real_addr_t()));\n    checkRangeSetInvariant(rangeSet2);\n  }\n\n  intersection1.add(rangeSet1);\n  intersection1.intersect(rangeSet2);\n  intersection2.add(rangeSet2);\n  intersection2.intersect(rangeSet1);\n\n  REQUIRE(intersection1 == intersection2);\n  checkRangeSetInvariant(intersection1);\n  checkRangeSetInvariant(intersection2);\n\n  for (QBDI::Range<int> r : intersection1.getRanges()) {\n    REQUIRE(true == rangeSet1.contains(r));\n    REQUIRE(true == rangeSet2.contains(r));\n  }\n}\n\nTEST_CASE(\"Range-IntersectionAndOverlaps\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  static const unsigned N = 100;\n  std::vector<QBDI::Range<int>> testRanges;\n\n  for (unsigned i = 0; i < N; i++) {\n    int start = get_random() % 900;\n    int end = start + get_random() % 100 + 1;\n    QBDI::Range<int> newRange{start, end, QBDI::real_addr_t()};\n\n    for (const QBDI::Range<int> &r : testRanges) {\n      QBDI::RangeSet<int> set;\n      set.add(newRange);\n      set.add(r);\n\n      // if the two range overlaps, the\n      // size of set must be lesser than the sum of the size\n      REQUIRE((set.size() < newRange.size() + r.size()) ==\n              newRange.overlaps(r));\n      REQUIRE(r.overlaps(newRange) == newRange.overlaps(r));\n      checkRangeSetInvariant(set);\n    }\n    testRanges.push_back(newRange);\n  }\n}\n"
  },
  {
    "path": "test/API/VMTest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <catch2/catch_test_macros.hpp>\n#include <set>\n#include \"APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/PtrAuth.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/String.h\"\n\n#if defined(QBDI_ARCH_X86)\n#include \"X86/VMTest_X86.h\"\n#elif defined(QBDI_ARCH_X86_64)\n#include \"X86_64/VMTest_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"ARM/VMTest_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"AARCH64/VMTest_AARCH64.h\"\n#else\n#error \"Architecture not supported\"\n#endif\n\n#define FAKE_RET_ADDR 0x666\n\nQBDI_DISABLE_ASAN QBDI_NOINLINE int dummyFun0() { return 42; }\n\nQBDI_DISABLE_ASAN QBDI_NOINLINE int dummyFun1(int arg0) { return arg0; }\n\nQBDI_DISABLE_ASAN QBDI_NOINLINE int dummyFun4(int arg0, int arg1, int arg2,\n                                              int arg3) {\n  return arg0 + arg1 + arg2 + arg3;\n}\n\nQBDI_DISABLE_ASAN QBDI_NOINLINE int dummyFun5(int arg0, int arg1, int arg2,\n                                              int arg3, int arg4) {\n  return arg0 + arg1 + arg2 + arg3 + arg4;\n}\n\nQBDI_DISABLE_ASAN QBDI_NOINLINE int dummyFun8(int arg0, int arg1, int arg2,\n                                              int arg3, int arg4, int arg5,\n                                              int arg6, int arg7) {\n  return arg0 + arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + arg7;\n}\n\nQBDI_DISABLE_ASAN QBDI_NOINLINE int dummyFunCall(int arg0) {\n  // use simple BUT multiplatform functions to test external calls\n  uint8_t *useless = (uint8_t *)QBDI::alignedAlloc(256, 16);\n  if (useless) {\n    *(int *)useless = arg0;\n    QBDI::alignedFree(useless);\n  }\n  return dummyFun1(arg0);\n}\n\nQBDI_DISABLE_ASAN QBDI_NOINLINE int dummyFunBB(int arg0, int arg1, int arg2,\n                                               int (*f0)(int), int (*f1)(int),\n                                               int (*f2)(int)) {\n  int r = 0;\n  if (arg0 & 1) {\n    r = f1(f0(arg1)) + arg2;\n    r ^= arg0;\n  } else {\n    r = f0(f1(arg2)) + arg1;\n    r ^= arg0;\n  }\n  r = f2(r + arg0 + arg1 + arg2);\n  if (arg0 & 2) {\n    r += f1(f0(arg2 + r)) + arg1;\n    r ^= arg0;\n  } else {\n    r += f0(f1(arg1 + r)) + arg2;\n    r ^= arg0;\n  }\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-Call0\") {\n  QBDI::simulateCall(state, FAKE_RET_ADDR);\n\n  vm.run((QBDI::rword)dummyFun0, (QBDI::rword)FAKE_RET_ADDR);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)42);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-Call1\") {\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {42});\n\n  vm.run((QBDI::rword)dummyFun1, (QBDI::rword)FAKE_RET_ADDR);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)dummyFun1(42));\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-Call4\") {\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 5});\n\n  vm.run((QBDI::rword)dummyFun4, (QBDI::rword)FAKE_RET_ADDR);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)dummyFun4(1, 2, 3, 5));\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-Call5\") {\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 5, 8});\n\n  vm.run((QBDI::rword)dummyFun5, (QBDI::rword)FAKE_RET_ADDR);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)dummyFun5(1, 2, 3, 5, 8));\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-Call8\") {\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 5, 8, 13, 21, 34});\n\n  vm.run((QBDI::rword)dummyFun8, (QBDI::rword)FAKE_RET_ADDR);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)dummyFun8(1, 2, 3, 5, 8, 13, 21, 34));\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-ExternalCall\") {\n\n  dummyFunCall(42);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {42});\n\n  vm.run((QBDI::rword)dummyFunCall, (QBDI::rword)FAKE_RET_ADDR);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)dummyFun1(42));\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-SwitchStackAndCall8\") {\n  QBDI_GPR_SET(state, QBDI::REG_SP, 0);\n\n  QBDI::rword ret;\n  vm.switchStackAndCall(&ret, (QBDI::rword)dummyFun8,\n                        {1, 2, 3, 5, 8, 13, 21, 34});\n  REQUIRE(ret == (QBDI::rword)dummyFun8(1, 2, 3, 5, 8, 13, 21, 34));\n\n  SUCCEED();\n}\n\nQBDI::VMAction countInstruction(QBDI::VMInstanceRef vm,\n                                QBDI::GPRState *gprState,\n                                QBDI::FPRState *fprState, void *data) {\n  *((uint32_t *)data) += 1;\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction evilCbk(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                       QBDI::FPRState *fprState, void *data) {\n  const QBDI::InstAnalysis *ana = vm->getInstAnalysis();\n  CHECK(ana->mnemonic != nullptr);\n  CHECK(ana->disassembly != nullptr);\n  CHECK(ana->operands == nullptr);\n  QBDI::rword *info = (QBDI::rword *)data;\n  QBDI::rword cval = QBDI_GPR_GET(gprState, QBDI::REG_RETURN);\n  // should never be reached (because we stop VM after value is incremented)\n  if (info[1] != 0) {\n    // if we failed this test, just return a fixed value\n    QBDI_GPR_SET(gprState, QBDI::REG_RETURN, 0x21);\n  }\n  // return register is being set with our return value\n  if (cval == (satanicFun(info[0]))) {\n    info[1]++;\n    return QBDI::VMAction::STOP;\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\n/* This test is used to ensure that addCodeAddrCB is not broken */\nTEST_CASE_METHOD(APITest, \"VMTest-Breakpoint\") {\n  uint32_t counter = 0;\n  QBDI::rword retval = 0;\n  vm.addCodeAddrCB((QBDI::rword)dummyFun0, QBDI::InstPosition::PREINST,\n                   countInstruction, &counter);\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(counter == 1u);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstCallback\") {\n  QBDI::rword info[2] = {42, 0};\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {info[0]});\n\n  uint32_t instrId = vm.addCodeCB(QBDI::InstPosition::POSTINST, evilCbk, &info);\n\n  bool ran = vm.run((QBDI::rword)satanicFun, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)satanicFun(info[0]));\n  REQUIRE(info[1] == (QBDI::rword)1);\n\n  bool success = vm.deleteInstrumentation(instrId);\n  REQUIRE(success);\n\n  SUCCEED();\n}\n\nQBDI::VMAction evilMnemCbk(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                           QBDI::FPRState *fprState, void *data) {\n  QBDI::rword *info = (QBDI::rword *)data;\n  if (info[0] >= MNEM_COUNT)\n    return QBDI::VMAction::CONTINUE;\n\n  // get instruction metadata\n  const QBDI::InstAnalysis *ana =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION | QBDI::ANALYSIS_OPERANDS);\n  // validate mnemonic\n  CHECKED_IF(QBDI::String::startsWith(MNEM_CMP, ana->mnemonic)) {\n    info[0]++; // CMP count\n    info[1]++;\n    // validate address\n    CHECKED_IF(ana->address >= (QBDI::rword)&satanicFun)\n    CHECKED_IF(ana->address < (((QBDI::rword)&satanicFun) + 0x100))\n    info[1]++;\n    // validate inst size\n    const struct TestInst &currentInst = TestInsts[info[0] - 1];\n#if defined(QBDI_ARCH_X86) || defined(QBDI_ARCH_X86_64)\n    CHECKED_IF(ana->instSize == currentInst.instSize) {\n#else\n    {\n#endif\n      info[1]++;\n    }\n    // validate instruction type (kinda...)\n    if (currentInst.isCompare) {\n      // CHECKED_IF doesn't support && operator\n      CHECKED_IF(!ana->isBranch)\n      CHECKED_IF(!ana->isCall)\n      CHECKED_IF(!ana->isReturn)\n      CHECKED_IF(ana->isCompare)\n      info[1]++;\n    }\n    CHECKED_IF(ana->flagsAccess == currentInst.flagsAccess) { info[1]++; }\n    // validate number of analyzed operands\n    CHECKED_IF(ana->numOperands == currentInst.numOperands) { info[1]++; }\n    // validate operands\n    CHECKED_IF(ana->operands != nullptr) {\n      info[1]++;\n      for (unsigned idx = 0;\n           idx < std::min(ana->numOperands, currentInst.numOperands); idx++) {\n        INFO(\"For operand \" << idx);\n        const QBDI::OperandAnalysis &cmpOp = currentInst.operands[idx];\n        const QBDI::OperandAnalysis &op = ana->operands[idx];\n        CHECKED_IF(op.type == cmpOp.type) { info[1]++; }\n        if (op.type == QBDI::OPERAND_IMM) {\n          CHECKED_IF(op.value == cmpOp.value) { info[1]++; }\n        }\n        if (op.regName == nullptr && cmpOp.regName == nullptr) {\n          info[1]++;\n        } else {\n          CHECKED_IF(op.regName != nullptr)\n          CHECKED_IF(cmpOp.regName != nullptr)\n          CHECKED_IF(std::string(op.regName) == std::string(cmpOp.regName))\n          info[1]++;\n        }\n        CHECKED_IF(op.size == cmpOp.size) { info[1]++; }\n        CHECKED_IF(op.regCtxIdx == cmpOp.regCtxIdx) { info[1]++; }\n        CHECKED_IF(op.regOff == cmpOp.regOff) { info[1]++; }\n        CHECKED_IF(op.regAccess == cmpOp.regAccess) { info[1]++; }\n      }\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-MnemCallback\") {\n#ifdef QBDI_ARCH_ARM\n  bool isThumb = ((((QBDI::rword)satanicFun) & 1) == 1);\n  // not compatible with Thumb\n  if (isThumb) {\n    return;\n  }\n#endif\n\n  QBDI::rword info[3] = {0, 0, 42};\n  QBDI::rword retval = 0;\n  const char *noop = MNEM_CMP;\n\n  uint32_t instrId =\n      vm.addMnemonicCB(noop, QBDI::InstPosition::PREINST, evilMnemCbk, &info);\n\n  bool ran = vm.call(&retval, (QBDI::rword)satanicFun, {info[2]});\n  REQUIRE(ran);\n\n  CHECK(retval == (QBDI::rword)satanicFun(info[2]));\n  // TODO: try to find a way to support windows\n#ifdef QBDI_PLATFORM_WINDOWS\n  CHECK(info[1] == (QBDI::rword)0);\n#else\n  CHECK(info[0] == MNEM_COUNT);\n  CHECK(info[1] == (QBDI::rword)MNEM_VALIDATION);\n#endif\n\n  bool success = vm.deleteInstrumentation(instrId);\n  REQUIRE(success);\n\n  SUCCEED();\n}\n\nQBDI::VMAction checkTransfer(QBDI::VMInstanceRef vm, const QBDI::VMState *state,\n                             QBDI::GPRState *gprState, QBDI::FPRState *fprState,\n                             void *data) {\n  int *s = (int *)data;\n  if (state->event == QBDI::VMEvent::EXEC_TRANSFER_CALL) {\n    REQUIRE((*s % 2) == 0);\n    REQUIRE(QBDI::strip_ptrauth(reinterpret_cast<QBDI::rword>(dummyFun1)) ==\n            state->sequenceStart);\n    *s += 1;\n  } else if (state->event == QBDI::VMEvent::EXEC_TRANSFER_RETURN) {\n    REQUIRE((*s % 2) == 1);\n    REQUIRE(QBDI::strip_ptrauth(reinterpret_cast<QBDI::rword>(dummyFun1)) ==\n            state->sequenceStart);\n    *s += 1;\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-VMEvent_ExecTransfer\") {\n  int s = 0;\n\n  bool instrumented = vm.addInstrumentedModuleFromAddr(\n      reinterpret_cast<QBDI::rword>(dummyFunBB));\n  REQUIRE(instrumented);\n  vm.removeInstrumentedRange(reinterpret_cast<QBDI::rword>(dummyFun1),\n                             reinterpret_cast<QBDI::rword>(dummyFun1) + 1);\n\n  uint32_t id = vm.addVMEventCB(QBDI::VMEvent::EXEC_TRANSFER_CALL,\n                                checkTransfer, (void *)&s);\n  REQUIRE(id != QBDI::INVALID_EVENTID);\n  id = vm.addVMEventCB(QBDI::VMEvent::EXEC_TRANSFER_RETURN, checkTransfer,\n                       (void *)&s);\n  REQUIRE(id != QBDI::INVALID_EVENTID);\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, reinterpret_cast<QBDI::rword>(dummyFunBB),\n                     {0, 0, 0, reinterpret_cast<QBDI::rword>(dummyFun1),\n                      reinterpret_cast<QBDI::rword>(dummyFun1),\n                      reinterpret_cast<QBDI::rword>(dummyFun1)});\n  REQUIRE(ran);\n  REQUIRE(retval == (QBDI::rword)0);\n  REQUIRE(10 == s);\n  vm.deleteAllInstrumentations();\n}\n\nstruct CheckBasicBlockData {\n  bool waitingEnd;\n  QBDI::rword BBStart;\n  QBDI::rword BBEnd;\n  size_t count;\n};\n\nstatic QBDI::VMAction checkBasicBlock(QBDI::VMInstanceRef vm,\n                                      const QBDI::VMState *vmState,\n                                      QBDI::GPRState *gprState,\n                                      QBDI::FPRState *fprState, void *data_) {\n  CheckBasicBlockData *data = static_cast<CheckBasicBlockData *>(data_);\n  CHECK((vmState->event & (QBDI::BASIC_BLOCK_ENTRY | QBDI::BASIC_BLOCK_EXIT)) !=\n        0);\n  CHECK((vmState->event & (QBDI::BASIC_BLOCK_ENTRY | QBDI::BASIC_BLOCK_EXIT)) !=\n        (QBDI::BASIC_BLOCK_ENTRY | QBDI::BASIC_BLOCK_EXIT));\n  if (vmState->event & QBDI::BASIC_BLOCK_ENTRY) {\n    CHECK_FALSE(data->waitingEnd);\n    CHECK(vmState->basicBlockStart == vmState->sequenceStart);\n    *data = {true, vmState->basicBlockStart, vmState->basicBlockEnd,\n             data->count};\n  } else if (vmState->event & QBDI::BASIC_BLOCK_EXIT) {\n    CHECK(data->waitingEnd);\n    CHECK(data->BBStart == vmState->basicBlockStart);\n    CHECK(data->BBEnd == vmState->basicBlockEnd);\n    CHECK(vmState->basicBlockEnd == vmState->sequenceEnd);\n    data->waitingEnd = false;\n    data->count++;\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-VMEvent_BasicBlock\") {\n  CheckBasicBlockData data{false, 0, 0, 0};\n  vm.addVMEventCB(QBDI::BASIC_BLOCK_ENTRY | QBDI::BASIC_BLOCK_EXIT,\n                  checkBasicBlock, &data);\n\n  // backup GPRState to have the same state before each run\n  QBDI::GPRState backup = *(vm.getGPRState());\n\n  for (QBDI::rword j = 0; j < 4; j++) {\n    for (QBDI::rword i = 0; i < 8; i++) {\n      QBDI_DEBUG(\"Begin Loop iteration {} {}\", j, i);\n      vm.setGPRState(&backup);\n\n      data.waitingEnd = false;\n      data.count = 0;\n      QBDI::rword retval;\n      bool ran =\n          vm.call(&retval, reinterpret_cast<QBDI::rword>(dummyFunBB),\n                  {i ^ j, 5, 13, reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1)});\n      CHECK(ran);\n      CHECK_FALSE(data.waitingEnd);\n      CHECK(data.count != 0);\n    }\n    vm.clearAllCache();\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-CacheInvalidation\") {\n  uint32_t count1 = 0;\n  uint32_t count2 = 0;\n\n  bool instrumented =\n      vm.addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n  uint32_t instr1 =\n      vm.addCodeCB(QBDI::InstPosition::POSTINST, countInstruction, &count1);\n\n  count1 = 0;\n  count2 = 0;\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4});\n  bool ran = vm.run((QBDI::rword)dummyFun4, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)10);\n  REQUIRE((uint32_t)0 != count1);\n  REQUIRE((uint32_t)0 == count2);\n\n  uint32_t instr2 = vm.addCodeRangeCB(\n      (QBDI::rword)&dummyFun5, ((QBDI::rword)&dummyFun5) + 64,\n      QBDI::InstPosition::POSTINST, countInstruction, &count2);\n\n  count1 = 0;\n  count2 = 0;\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4, 5});\n  ran = vm.run((QBDI::rword)dummyFun5, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)15);\n  REQUIRE((uint32_t)0 != count1);\n  REQUIRE((uint32_t)0 != count2);\n\n  vm.deleteInstrumentation(instr1);\n\n  count1 = 0;\n  count2 = 0;\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4});\n  ran = vm.run((QBDI::rword)dummyFun4, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)10);\n  REQUIRE((uint32_t)0 == count1);\n  REQUIRE((uint32_t)0 == count2);\n\n  count1 = 0;\n  count2 = 0;\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4, 5});\n  ran = vm.run((QBDI::rword)dummyFun5, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)15);\n  REQUIRE((uint32_t)0 == count1);\n  REQUIRE((uint32_t)0 != count2);\n\n  instr1 =\n      vm.addCodeCB(QBDI::InstPosition::POSTINST, countInstruction, &count1);\n\n  count1 = 0;\n  count2 = 0;\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4, 5});\n  ran = vm.run((QBDI::rword)dummyFun5, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)15);\n  REQUIRE((uint32_t)0 != count1);\n  REQUIRE((uint32_t)0 != count2);\n\n  vm.deleteInstrumentation(instr2);\n\n  count1 = 0;\n  count2 = 0;\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4});\n  ran = vm.run((QBDI::rword)dummyFun4, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)10);\n  REQUIRE((uint32_t)0 != count1);\n  REQUIRE((uint32_t)0 == count2);\n\n  count1 = 0;\n  count2 = 0;\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4, 5});\n  ran = vm.run((QBDI::rword)dummyFun5, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)15);\n  REQUIRE((uint32_t)0 != count1);\n  REQUIRE((uint32_t)0 == count2);\n}\n\nstruct FunkyInfo {\n  uint32_t instID;\n  uint32_t count;\n};\n\nQBDI::VMAction funkyCountInstruction(QBDI::VMInstanceRef vm,\n                                     QBDI::GPRState *gprState,\n                                     QBDI::FPRState *fprState, void *data) {\n  FunkyInfo *info = (FunkyInfo *)data;\n\n  const QBDI::InstAnalysis *instAnalysis1 =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION);\n  vm->deleteInstrumentation(info->instID);\n  const QBDI::InstAnalysis *instAnalysis2 =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION);\n  info->instID = vm->addCodeRangeCB(QBDI_GPR_GET(gprState, QBDI::REG_PC),\n                                    QBDI_GPR_GET(gprState, QBDI::REG_PC) + 10,\n                                    QBDI::InstPosition::POSTINST,\n                                    funkyCountInstruction, data);\n  const QBDI::InstAnalysis *instAnalysis3 =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION);\n  // instAnalysis1, instAnalysis2 and instAnalysis3 should be the same pointer\n  // because the cache flush initiated by deleteInstrumentation and\n  // addCodeRangeCB is delayed.\n  if (instAnalysis1 == instAnalysis2 && instAnalysis2 == instAnalysis3) {\n    info->count += 1;\n  }\n\n  // instAnalysis3 should not have disassembly information, but instAnalysis4\n  // and instAnalysis5 should.\n  CHECK(instAnalysis3->disassembly == nullptr);\n  CHECK(instAnalysis3->operands == nullptr);\n  const QBDI::InstAnalysis *instAnalysis4 = vm->getInstAnalysis(\n      QBDI::ANALYSIS_INSTRUCTION | QBDI::ANALYSIS_DISASSEMBLY);\n  CHECK(instAnalysis4->disassembly != nullptr);\n  CHECK(instAnalysis4->operands == nullptr);\n  const QBDI::InstAnalysis *instAnalysis5 =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION);\n  CHECK(instAnalysis5->disassembly != nullptr);\n  CHECK(instAnalysis5->operands == nullptr);\n\n  return QBDI::VMAction::BREAK_TO_VM;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-DelayedCacheFlush\") {\n  uint32_t count = 0;\n  FunkyInfo info = FunkyInfo{0, 0};\n\n  bool instrumented =\n      vm.addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, countInstruction, &count);\n  info.instID = vm.addCodeRangeCB(\n      (QBDI::rword)dummyFun4, ((QBDI::rword)dummyFun4) + 10,\n      QBDI::InstPosition::POSTINST, funkyCountInstruction, &info);\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR, {1, 2, 3, 4});\n  bool ran = vm.run((QBDI::rword)dummyFun4, (QBDI::rword)FAKE_RET_ADDR);\n  REQUIRE(ran);\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)10);\n  REQUIRE(count == info.count);\n}\n\nstruct PriorityDataCall {\n  QBDI::rword addr;\n  QBDI::InstPosition pos;\n  int priority;\n\n  PriorityDataCall(QBDI::rword addr, QBDI::InstPosition pos, int priority)\n      : addr(addr), pos(pos), priority(priority) {}\n};\n\nstatic std::vector<QBDI::InstrRuleDataCBK>\npriorityInstrCB(QBDI::VMInstanceRef vm, const QBDI::InstAnalysis *inst,\n                void *data_) {\n  std::vector<QBDI::InstrRuleDataCBK> r;\n\n  r.emplace_back(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::PREINST, -100);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      data_, -100);\n\n  r.emplace_back(\n      QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::POSTINST, 0);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      data_, 0);\n\n  r.emplace_back(\n      QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::POSTINST, 100);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      data_, 100);\n\n  r.emplace_back(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::PREINST, 100);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      data_, 100);\n\n  r.emplace_back(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::PREINST, 0);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      data_, 0);\n\n  r.emplace_back(\n      QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::POSTINST, -100);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      data_, -100);\n\n  return r;\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-Priority\") {\n  std::vector<PriorityDataCall> callList;\n  QBDI::rword retval = 0;\n\n  vm.addCodeCB(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::PREINST, -10);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      &callList, -10);\n\n  vm.addCodeCB(\n      QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::POSTINST, -67);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      &callList, -67);\n\n  vm.addCodeCB(\n      QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::POSTINST, 56);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      &callList, 56);\n\n  vm.addInstrRule(priorityInstrCB, QBDI::ANALYSIS_INSTRUCTION, &callList);\n\n  vm.addCodeCB(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::PREINST, 27);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      &callList, 27);\n\n  vm.addCodeCB(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((std::vector<PriorityDataCall> *)data)\n            ->emplace_back(\n                vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION)->address,\n                QBDI::InstPosition::PREINST, -77);\n\n        return QBDI::VMAction::CONTINUE;\n      },\n      &callList, -77);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n\n  REQUIRE(callList.size() >= 11);\n\n  for (size_t i = 1; i < callList.size(); i++) {\n    if (callList[i - 1].addr == callList[i].addr) {\n      if (callList[i - 1].pos == callList[i].pos) {\n        REQUIRE(callList[i - 1].priority >= callList[i].priority);\n      } else {\n        REQUIRE(callList[i - 1].pos == QBDI::InstPosition::PREINST);\n        REQUIRE(callList[i].pos == QBDI::InstPosition::POSTINST);\n      }\n    }\n  }\n\n  SUCCEED();\n}\n\nstruct SkipTestData {\n  uint8_t cbpre1;\n  uint8_t cbpre2;\n  uint8_t cbpre3;\n  uint8_t cbpost;\n  uint8_t cbnumpre;\n  uint8_t cbnumpost;\n};\n\nTEST_CASE_METHOD(APITest, \"VMTest-SKIP_INST\") {\n\n  SkipTestData data = {0};\n\n  QBDI::rword addr = genASM(SKIPTESTASM);\n\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpre1++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 100);\n  vm.addCodeCB(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbnumpre++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 0);\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpre2++;\n        return QBDI::VMAction::SKIP_INST;\n      },\n      &data, -100);\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpre3++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, -200);\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpost++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 0);\n  vm.addCodeCB(\n      QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbnumpost++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 0);\n\n  QBDI::rword retval = 0;\n  vm.call(&retval, (QBDI::rword)addr);\n  REQUIRE(data.cbpre1 == 1);\n  REQUIRE(data.cbpre2 == 1);\n  REQUIRE(data.cbpre3 == 0);\n  REQUIRE(data.cbpost == 1);\n  REQUIRE(data.cbnumpre == 3);\n  REQUIRE(data.cbnumpost == 3);\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-SKIP_PATCH\") {\n\n  SkipTestData data = {0};\n\n  QBDI::rword addr = genASM(SKIPTESTASM);\n\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpre1++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 100);\n  vm.addCodeCB(\n      QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbnumpre++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 0);\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpre2++;\n        return QBDI::VMAction::SKIP_PATCH;\n      },\n      &data, -100);\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::PREINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpre3++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, -200);\n  vm.addCodeAddrCB(\n      addr, QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbpost++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 0);\n  vm.addCodeCB(\n      QBDI::InstPosition::POSTINST,\n      [](QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n         QBDI::FPRState *fprState, void *data) -> QBDI::VMAction {\n        ((SkipTestData *)data)->cbnumpost++;\n        return QBDI::VMAction::CONTINUE;\n      },\n      &data, 0);\n\n  QBDI::rword retval = 0;\n  vm.call(&retval, (QBDI::rword)addr);\n  REQUIRE(data.cbpre1 == 1);\n  REQUIRE(data.cbpre2 == 1);\n  REQUIRE(data.cbpre3 == 0);\n  REQUIRE(data.cbpost == 0);\n  REQUIRE(data.cbnumpre == 3);\n  REQUIRE(data.cbnumpost == 2);\n}\n\n// Test copy/move constructor/assignment operator\n\nstruct MoveCallbackStruct {\n  QBDI::VMInstanceRef expectedRef;\n  bool allowedNewBlock;\n\n  bool reachEventCB;\n  bool reachInstCB;\n  bool reachInstrumentCB;\n  bool reachCB2;\n};\n\nstatic QBDI::VMAction allowedNewBlock(QBDI::VMInstanceRef vm,\n                                      const QBDI::VMState *state,\n                                      QBDI::GPRState *, QBDI::FPRState *,\n                                      void *data_) {\n  MoveCallbackStruct *data = static_cast<MoveCallbackStruct *>(data_);\n  CHECK(data->expectedRef == vm);\n  CHECK(\n      (data->allowedNewBlock or ((state->event & QBDI::BASIC_BLOCK_NEW) == 0)));\n\n  data->reachEventCB = true;\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic std::vector<QBDI::InstrRuleDataCBK>\ninstrumentCopyCB(QBDI::VMInstanceRef vm, const QBDI::InstAnalysis *inst,\n                 void *data_) {\n  MoveCallbackStruct *data = static_cast<MoveCallbackStruct *>(data_);\n  CHECK(data->expectedRef == vm);\n  CHECK(data->allowedNewBlock);\n\n  data->reachInstrumentCB = true;\n  return {};\n}\n\nstatic QBDI::VMAction verifyVMRef(QBDI::VMInstanceRef vm, QBDI::GPRState *,\n                                  QBDI::FPRState *, void *data_) {\n  MoveCallbackStruct *data = static_cast<MoveCallbackStruct *>(data_);\n  CHECK(data->expectedRef == vm);\n\n  data->reachInstCB = true;\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction verifyCB2(QBDI::VMInstanceRef vm, QBDI::GPRState *,\n                                QBDI::FPRState *, void *data_) {\n  MoveCallbackStruct *data = static_cast<MoveCallbackStruct *>(data_);\n  CHECK(data->expectedRef == vm);\n\n  data->reachCB2 = true;\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE(\"VMTest-MoveConstructor\") {\n\n  APITest vm1_;\n  std::unique_ptr<QBDI::VM> vm1 = std::make_unique<QBDI::VM>(vm1_.vm);\n  QBDI::VM *vm = vm1.get();\n\n  MoveCallbackStruct data{vm, true, false, false, false, false};\n\n  bool instrumented =\n      vm->addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n\n  vm->addCodeCB(QBDI::InstPosition::POSTINST, verifyVMRef, &data);\n  vm->addInstrRule(instrumentCopyCB, QBDI::ANALYSIS_INSTRUCTION, &data);\n  vm->addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                       QBDI::BASIC_BLOCK_NEW,\n                   allowedNewBlock, &data);\n\n  QBDI::rword retvalue;\n\n  QBDI_DEBUG(\"Execute dummyFun1 with the original VM\");\n  vm->call(&retvalue, (QBDI::rword)dummyFun1, {350});\n  REQUIRE(retvalue == 350);\n  REQUIRE(data.reachEventCB);\n  REQUIRE(data.reachInstCB);\n  REQUIRE(data.reachInstrumentCB);\n\n  data.reachEventCB = false;\n  data.reachInstCB = false;\n  data.reachInstrumentCB = false;\n  data.allowedNewBlock = false;\n\n  REQUIRE(vm == data.expectedRef);\n\n  // move vm\n  QBDI_DEBUG(\"Move the VM\");\n  QBDI::VM movedVM(std::move(*vm));\n  vm = nullptr;\n\n  vm1.reset();\n  REQUIRE(vm1.get() == nullptr);\n\n  REQUIRE(data.expectedRef != &movedVM);\n  data.expectedRef = &movedVM;\n\n  QBDI_DEBUG(\"Execute with the moved VM\");\n  movedVM.call(&retvalue, (QBDI::rword)dummyFun1, {780});\n\n  REQUIRE(retvalue == 780);\n  REQUIRE(data.reachEventCB);\n  REQUIRE(data.reachInstCB);\n  REQUIRE_FALSE(data.reachInstrumentCB);\n\n  data.allowedNewBlock = true;\n  movedVM.precacheBasicBlock((QBDI::rword)dummyFun0);\n  REQUIRE(data.reachInstrumentCB);\n}\n\nTEST_CASE(\"VMTest-CopyConstructor\") {\n\n  APITest vm1;\n  QBDI::VM *vm = &vm1.vm;\n\n  MoveCallbackStruct data{vm, true, false, false, false, false};\n\n  bool instrumented =\n      vm->addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n\n  vm->addCodeCB(QBDI::InstPosition::POSTINST, verifyVMRef, &data);\n  vm->addInstrRule(instrumentCopyCB, QBDI::ANALYSIS_INSTRUCTION, &data);\n  vm->addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                       QBDI::BASIC_BLOCK_NEW,\n                   allowedNewBlock, &data);\n\n  QBDI::rword retvalue;\n\n  QBDI_DEBUG(\"Execute dummyFun1 with the original VM\");\n  vm->call(&retvalue, (QBDI::rword)dummyFun1, {350});\n  REQUIRE(retvalue == 350);\n  REQUIRE(data.reachEventCB);\n  REQUIRE(data.reachInstCB);\n  REQUIRE(data.reachInstrumentCB);\n\n  data.reachEventCB = false;\n  data.reachInstCB = false;\n  data.reachInstrumentCB = false;\n  data.allowedNewBlock = false;\n\n  // copy vm\n  QBDI_DEBUG(\"Copy the VM\");\n  QBDI::VM movedVM(*vm);\n\n  REQUIRE(data.expectedRef != &movedVM);\n\n  QBDI_DEBUG(\"Execute second time with the original VM\");\n  vm->call(&retvalue, (QBDI::rword)dummyFun1, {620});\n  REQUIRE(retvalue == 620);\n  REQUIRE(data.reachEventCB);\n  REQUIRE(data.reachInstCB);\n  REQUIRE_FALSE(data.reachInstrumentCB);\n\n  data.reachEventCB = false;\n  data.reachInstCB = false;\n  data.reachInstrumentCB = false;\n  data.allowedNewBlock = true;\n  data.expectedRef = &movedVM;\n\n  QBDI_DEBUG(\"Execute with the copied VM\");\n  movedVM.call(&retvalue, (QBDI::rword)dummyFun1, {780});\n  REQUIRE(retvalue == 780);\n  REQUIRE(data.reachEventCB);\n  REQUIRE(data.reachInstCB);\n  REQUIRE(data.reachInstrumentCB);\n}\n\nTEST_CASE(\"VMTest-MoveAssignmentOperator\") {\n\n  APITest vm1__;\n  APITest vm2__;\n  std::unique_ptr<QBDI::VM> vm1_ = std::make_unique<QBDI::VM>(vm1__.vm);\n  std::unique_ptr<QBDI::VM> vm2_ = std::make_unique<QBDI::VM>(vm2__.vm);\n  QBDI::VM *vm1 = vm1_.get();\n  QBDI::VM *vm2 = vm2_.get();\n  REQUIRE(vm1 != vm2);\n\n  MoveCallbackStruct data1{vm1, true, false, false, false, false};\n  MoveCallbackStruct data2{vm2, true, false, false, false, false};\n\n  bool instrumented =\n      vm1->addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n  instrumented = vm2->addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n\n  vm1->addCodeCB(QBDI::InstPosition::POSTINST, verifyVMRef, &data1);\n  vm1->addInstrRule(instrumentCopyCB, QBDI::ANALYSIS_INSTRUCTION, &data1);\n  vm1->addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                        QBDI::BASIC_BLOCK_NEW,\n                    allowedNewBlock, &data1);\n\n  vm2->addCodeCB(QBDI::InstPosition::POSTINST, verifyVMRef, &data2);\n  vm2->addInstrRule(instrumentCopyCB, QBDI::ANALYSIS_INSTRUCTION, &data2);\n  vm2->addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                        QBDI::BASIC_BLOCK_NEW,\n                    allowedNewBlock, &data2);\n\n  QBDI::rword retvalue;\n\n  vm1->call(&retvalue, (QBDI::rword)dummyFun1, {350});\n  REQUIRE(retvalue == 350);\n  REQUIRE(data1.reachEventCB);\n  REQUIRE(data1.reachInstCB);\n  REQUIRE(data1.reachInstrumentCB);\n\n  data1.reachEventCB = false;\n  data1.reachInstCB = false;\n  data1.reachInstrumentCB = false;\n  data1.allowedNewBlock = false;\n\n  vm2->call(&retvalue, (QBDI::rword)dummyFun1, {670});\n  REQUIRE(retvalue == 670);\n  REQUIRE(data2.reachEventCB);\n  REQUIRE(data2.reachInstCB);\n  REQUIRE(data2.reachInstrumentCB);\n\n  data2.reachEventCB = false;\n  data2.reachInstCB = false;\n  data2.reachInstrumentCB = false;\n  data2.allowedNewBlock = false;\n\n  data1.expectedRef = vm2;\n  data2.expectedRef = nullptr;\n\n  // move vm\n  *vm2 = std::move(*vm1);\n  vm1 = nullptr;\n\n  vm1_.reset();\n  REQUIRE(vm1_.get() == nullptr);\n\n  vm2->call(&retvalue, (QBDI::rword)dummyFun1, {780});\n\n  REQUIRE(retvalue == 780);\n  REQUIRE(data1.reachEventCB);\n  REQUIRE(data1.reachInstCB);\n  REQUIRE_FALSE(data1.reachInstrumentCB);\n  REQUIRE_FALSE(data2.reachEventCB);\n  REQUIRE_FALSE(data2.reachInstCB);\n  REQUIRE_FALSE(data2.reachInstrumentCB);\n\n  data1.allowedNewBlock = true;\n  vm2->precacheBasicBlock((QBDI::rword)dummyFun0);\n  REQUIRE(data1.reachInstrumentCB);\n}\n\nTEST_CASE(\"VMTest-CopyAssignmentOperator\") {\n\n  APITest vm1_;\n  APITest vm2_;\n  QBDI::VM *vm1 = &vm1_.vm;\n  QBDI::VM *vm2 = &vm2_.vm;\n  REQUIRE(vm1 != vm2);\n\n  MoveCallbackStruct data1{vm1, true, false, false, false, false};\n  MoveCallbackStruct data2{vm2, true, false, false, false, false};\n\n  bool instrumented =\n      vm1->addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n  instrumented = vm2->addInstrumentedModuleFromAddr((QBDI::rword)&dummyFunCall);\n  REQUIRE(instrumented);\n\n  vm1->addCodeCB(QBDI::InstPosition::POSTINST, verifyVMRef, &data1);\n  vm1->addCodeCB(QBDI::InstPosition::POSTINST, verifyCB2, &data1);\n  vm1->addInstrRule(instrumentCopyCB, QBDI::ANALYSIS_INSTRUCTION, &data1);\n  vm1->addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                        QBDI::BASIC_BLOCK_NEW,\n                    allowedNewBlock, &data1);\n\n  vm2->addCodeCB(QBDI::InstPosition::POSTINST, verifyVMRef, &data2);\n  vm2->addInstrRule(instrumentCopyCB, QBDI::ANALYSIS_INSTRUCTION, &data2);\n  vm2->addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                        QBDI::BASIC_BLOCK_NEW,\n                    allowedNewBlock, &data2);\n\n  QBDI::rword retvalue;\n\n  vm1->call(&retvalue, (QBDI::rword)dummyFun1, {350});\n  REQUIRE(retvalue == 350);\n  REQUIRE(data1.reachEventCB);\n  REQUIRE(data1.reachInstCB);\n  REQUIRE(data1.reachInstrumentCB);\n  REQUIRE(data1.reachCB2);\n\n  data1.reachEventCB = false;\n  data1.reachInstCB = false;\n  data1.reachInstrumentCB = false;\n  data1.allowedNewBlock = false;\n  data1.reachCB2 = false;\n\n  vm2->call(&retvalue, (QBDI::rword)dummyFun1, {670});\n  REQUIRE(retvalue == 670);\n  REQUIRE(data2.reachEventCB);\n  REQUIRE(data2.reachInstCB);\n  REQUIRE(data2.reachInstrumentCB);\n  REQUIRE_FALSE(data2.reachCB2);\n\n  data2.reachEventCB = false;\n  data2.reachInstCB = false;\n  data2.reachInstrumentCB = false;\n  data2.allowedNewBlock = false;\n  data2.expectedRef = nullptr;\n\n  // copy vm\n  *vm2 = *vm1;\n\n  vm1->call(&retvalue, (QBDI::rword)dummyFun1, {780});\n  REQUIRE(retvalue == 780);\n  REQUIRE(data1.reachEventCB);\n  REQUIRE(data1.reachInstCB);\n  REQUIRE_FALSE(data1.reachInstrumentCB);\n  REQUIRE(data1.reachCB2);\n  REQUIRE_FALSE(data2.reachEventCB);\n  REQUIRE_FALSE(data2.reachInstCB);\n  REQUIRE_FALSE(data2.reachCB2);\n  REQUIRE_FALSE(data2.reachInstrumentCB);\n\n  data1.reachEventCB = false;\n  data1.reachInstCB = false;\n  data1.reachInstrumentCB = false;\n  data1.allowedNewBlock = true;\n  data1.expectedRef = vm2;\n  data1.reachCB2 = false;\n\n  vm2->call(&retvalue, (QBDI::rword)dummyFun1, {567});\n\n  REQUIRE(retvalue == 567);\n  REQUIRE(data1.reachEventCB);\n  REQUIRE(data1.reachInstCB);\n  REQUIRE(data1.reachInstrumentCB);\n  REQUIRE(data1.reachCB2);\n  REQUIRE_FALSE(data2.reachEventCB);\n  REQUIRE_FALSE(data2.reachInstCB);\n  REQUIRE_FALSE(data2.reachCB2);\n  REQUIRE_FALSE(data2.reachInstrumentCB);\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-VMEventLambda-VMcpy\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::VMCbLambda cbk = [&cbCalled](QBDI::VMInstanceRef, const QBDI::VMState *,\n                                     QBDI::GPRState *, QBDI::FPRState *) {\n    cbCalled = true;\n    return QBDI::VMAction::CONTINUE;\n  };\n\n  vm.addVMEventCB(\n      QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT | QBDI::BASIC_BLOCK_NEW, cbk);\n\n  // copy constructor\n  QBDI::VM vm2 = vm;\n\n  // copy operator\n  QBDI::VM vm3;\n  vm3.precacheBasicBlock((QBDI::rword)dummyFun0);\n  vm3 = vm;\n\n  vm.deleteAllInstrumentations();\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm2.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm3.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstrRuleCbLambda-VMCpy\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n\n  vm.addInstrRule(\n      [&cbCalled](QBDI::VMInstanceRef, const QBDI::InstAnalysis *)\n          -> std::vector<QBDI::InstrRuleDataCBK> {\n        cbCalled = true;\n        return {};\n      },\n      QBDI::ANALYSIS_INSTRUCTION);\n\n  // copy constructor\n  QBDI::VM vm2 = vm;\n\n  // copy operator\n  QBDI::VM vm3;\n  vm3.precacheBasicBlock((QBDI::rword)dummyFun0);\n  vm3 = vm;\n\n  vm.deleteAllInstrumentations();\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm2.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm3.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstCbLambda-VMCpy\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n\n  vm.addCodeCB(\n      QBDI::InstPosition::PREINST,\n      [&cbCalled](QBDI::VMInstanceRef, QBDI::GPRState *, QBDI::FPRState *) {\n        cbCalled = true;\n        return QBDI::VMAction::CONTINUE;\n      });\n\n  // copy constructor\n  QBDI::VM vm2 = vm;\n\n  // copy operator\n  QBDI::VM vm3;\n  vm3.precacheBasicBlock((QBDI::rword)dummyFun0);\n  vm3 = vm;\n\n  vm.deleteAllInstrumentations();\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm2.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm3.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-VMEventLambda\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::VMCbLambda cbk = [&cbCalled](QBDI::VMInstanceRef, const QBDI::VMState *,\n                                     QBDI::GPRState *, QBDI::FPRState *) {\n    cbCalled = true;\n    return QBDI::VMAction::CONTINUE;\n  };\n\n  vm.addVMEventCB(\n      QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT | QBDI::BASIC_BLOCK_NEW, cbk);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                      QBDI::BASIC_BLOCK_NEW,\n                  std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstrRuleCbLambda-addInstrRule\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::InstrRuleCbLambda cbk =\n      [&cbCalled](\n          QBDI::VMInstanceRef,\n          const QBDI::InstAnalysis *) -> std::vector<QBDI::InstrRuleDataCBK> {\n    cbCalled = true;\n    return {};\n  };\n\n  vm.addInstrRule(cbk, QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addInstrRule(std::move(cbk), QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstrRuleCbLambda-addInstrRuleRange\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::InstrRuleCbLambda cbk =\n      [&cbCalled](\n          QBDI::VMInstanceRef,\n          const QBDI::InstAnalysis *) -> std::vector<QBDI::InstrRuleDataCBK> {\n    cbCalled = true;\n    return {};\n  };\n\n  vm.addInstrRuleRange((QBDI::rword)dummyFun0, (QBDI::rword)dummyFun0 + 0x100,\n                       cbk, QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addInstrRuleRange((QBDI::rword)dummyFun0, (QBDI::rword)dummyFun0 + 0x100,\n                       std::move(cbk), QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstrRuleCbLambda-addInstrRuleRangeSet\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::InstrRuleCbLambda cbk =\n      [&cbCalled](\n          QBDI::VMInstanceRef,\n          const QBDI::InstAnalysis *) -> std::vector<QBDI::InstrRuleDataCBK> {\n    cbCalled = true;\n    return {};\n  };\n  QBDI::RangeSet<QBDI::rword> s;\n  s.add({(QBDI::rword)dummyFun0, (QBDI::rword)dummyFun0 + 0x100,\n         QBDI::auth_addr_t()});\n\n  vm.addInstrRuleRangeSet(s, cbk, QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addInstrRuleRangeSet(s, std::move(cbk), QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstCbLambda-addMnemonicCB\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::InstCbLambda cbk = [&cbCalled](QBDI::VMInstanceRef, QBDI::GPRState *,\n                                       QBDI::FPRState *) {\n    cbCalled = true;\n    return QBDI::VMAction::CONTINUE;\n  };\n  vm.addMnemonicCB(\"*\", QBDI::InstPosition::PREINST, cbk);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addMnemonicCB(\"*\", QBDI::InstPosition::PREINST, std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstCbLambda-addCodeCB\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::InstCbLambda cbk = [&cbCalled](QBDI::VMInstanceRef, QBDI::GPRState *,\n                                       QBDI::FPRState *) {\n    cbCalled = true;\n    return QBDI::VMAction::CONTINUE;\n  };\n  vm.addCodeCB(QBDI::InstPosition::PREINST, cbk);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addCodeCB(QBDI::InstPosition::PREINST, std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstCbLambda-addCodeAddrCB\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::InstCbLambda cbk = [&cbCalled](QBDI::VMInstanceRef, QBDI::GPRState *,\n                                       QBDI::FPRState *) {\n    cbCalled = true;\n    return QBDI::VMAction::CONTINUE;\n  };\n  vm.addCodeAddrCB((QBDI::rword)dummyFun0, QBDI::InstPosition::PREINST, cbk);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addCodeAddrCB((QBDI::rword)dummyFun0, QBDI::InstPosition::PREINST,\n                   std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstCbLambda-addCodeRangeCB\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  QBDI::InstCbLambda cbk = [&cbCalled](QBDI::VMInstanceRef, QBDI::GPRState *,\n                                       QBDI::FPRState *) {\n    cbCalled = true;\n    return QBDI::VMAction::CONTINUE;\n  };\n  vm.addCodeRangeCB((QBDI::rword)dummyFun0, (QBDI::rword)dummyFun0 + 0x100,\n                    QBDI::InstPosition::PREINST, cbk);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  cbCalled = false;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n\n  vm.addCodeRangeCB((QBDI::rword)dummyFun0, (QBDI::rword)dummyFun0 + 0x100,\n                    QBDI::InstPosition::PREINST, std::move(cbk));\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InstCbLambda-InstrRuleDataCBK\") {\n  QBDI::rword retval;\n  bool cbCalled = false;\n  uint32_t count1 = 0;\n  uint32_t count2 = 0;\n  uint32_t *count = &count1;\n\n  QBDI::InstrRuleCbLambda cbk =\n      [&cbCalled, &count](\n          QBDI::VMInstanceRef,\n          const QBDI::InstAnalysis *) -> std::vector<QBDI::InstrRuleDataCBK> {\n    return {{QBDI::InstPosition::PREINST,\n             [&cbCalled, count](QBDI::VMInstanceRef, QBDI::GPRState *,\n                                QBDI::FPRState *) {\n               cbCalled = true;\n               (*count)++;\n               return QBDI::VMAction::CONTINUE;\n             }}};\n  };\n\n  vm.addInstrRule(cbk, QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n  REQUIRE(count1 != 0);\n  REQUIRE(count2 == 0);\n\n  uint32_t backcount1 = count1;\n  cbCalled = false;\n  count1 = 0;\n  count = &count2;\n  vm.deleteAllInstrumentations();\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(!cbCalled);\n  REQUIRE(count1 == 0);\n  REQUIRE(count2 == 0);\n\n  vm.addInstrRule(std::move(cbk), QBDI::ANALYSIS_INSTRUCTION);\n\n  vm.call(&retval, (QBDI::rword)dummyFun0);\n  REQUIRE(retval == (QBDI::rword)42);\n  REQUIRE(cbCalled);\n  REQUIRE(count1 == 0);\n  REQUIRE(count2 == backcount1);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-InvalidInstruction\") {\n  auto tc = TestCode[\"VMTest-InvalidInstruction\"];\n  auto code = tc.code;\n  if (code.empty()) {\n    return;\n  }\n  auto start = (QBDI::rword)code.data();\n  auto stop = (QBDI::rword)(code.data() + code.size());\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR);\n\n  // Instrument the whole code but only execute what is valid\n  vm.addInstrumentedRange(start, stop);\n  bool ran = vm.run(start, start + tc.size);\n  REQUIRE(ran);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-BreakingInstruction\") {\n  auto tc = TestCode[\"VMTest-BreakingInstruction\"];\n  auto code = tc.code;\n  if (code.empty()) {\n    return;\n  }\n  auto start = (QBDI::rword)code.data();\n\n  // Instrument only a part of the code\n  vm.addInstrumentedRange(start, start + tc.size);\n\n  // set the current sequence\n  int countBB = 0;\n  vm.addVMEventCB(QBDI::VMEvent::BASIC_BLOCK_NEW,\n                  [&countBB](QBDI::VMInstanceRef vm,\n                             const QBDI::VMState *vmState, QBDI::GPRState *,\n                             QBDI::FPRState *) {\n                    countBB++;\n                    return QBDI::VMAction::CONTINUE;\n                  });\n  QBDI::rword retval;\n  bool ran = vm.call(&retval, start);\n  REQUIRE(ran);\n  REQUIRE(countBB == 2);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-SelfModifyingCode1\") {\n  auto tc = TestCode[\"VMTest-SelfModifyingCode1\"];\n  auto code = tc.code;\n  if (code.empty()) {\n    return;\n  }\n  auto start = (QBDI::rword)code.data();\n  auto stop = (QBDI::rword)(code.data() + code.size());\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR);\n\n  vm.addInstrumentedRange(start, stop);\n  bool ran = vm.run(start, FAKE_RET_ADDR);\n  REQUIRE(ran);\n\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)42);\n\n  SUCCEED();\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-SelfModifyingCode2\") {\n  /**\n   * Test a strategy to handle self modifying code. Add a callback on write in\n   * the current basic block and invalid the cache if so.\n   * */\n  auto tc = TestCode[\"VMTest-SelfModifyingCode2\"];\n  auto code = tc.code;\n  if (code.empty()) {\n    return;\n  }\n  auto start = (QBDI::rword)code.data();\n  auto stop = (QBDI::rword)(code.data() + code.size());\n\n  QBDI::simulateCall(state, FAKE_RET_ADDR);\n\n  vm.addInstrumentedRange(start, stop);\n\n  QBDI::RangeSet<QBDI::rword> instrumentedRange{};\n  QBDI::Range<QBDI::rword> currentSeq{0, 0, QBDI::real_addr_t()};\n\n  // set the current sequence\n  vm.addVMEventCB(QBDI::VMEvent::SEQUENCE_ENTRY,\n                  [&instrumentedRange, &currentSeq](\n                      QBDI::VMInstanceRef vm, const QBDI::VMState *vmState,\n                      QBDI::GPRState *, QBDI::FPRState *) {\n                    currentSeq = {vmState->sequenceStart, vmState->sequenceEnd,\n                                  QBDI::real_addr_t()};\n                    instrumentedRange.add(currentSeq);\n                    return QBDI::VMAction::CONTINUE;\n                  });\n\n  // Detect self modifing code in already cached code\n  vm.addMemAccessCB(\n      QBDI::MemoryAccessType::MEMORY_WRITE,\n      [&instrumentedRange, &currentSeq](QBDI::VMInstanceRef vm,\n                                        QBDI::GPRState *, QBDI::FPRState *) {\n        for (const auto &acc : vm->getInstMemoryAccess()) {\n          if ((acc.type & QBDI::MemoryAccessType::MEMORY_WRITE) == 0) {\n            continue;\n          }\n          if ((acc.flags & QBDI::MemoryAccessFlags::MEMORY_UNKNOWN_SIZE) != 0) {\n            continue;\n          }\n          if (instrumentedRange.overlaps({acc.accessAddress,\n                                          acc.accessAddress + acc.size,\n                                          QBDI::real_addr_t()})) {\n            // the access override a code address, clear the cache\n            vm->clearCache(acc.accessAddress, acc.accessAddress + acc.size);\n            // if the access is in the current sequence, schedule\n            // the clear now\n            if (currentSeq.overlaps({acc.accessAddress,\n                                     acc.accessAddress + acc.size,\n                                     QBDI::real_addr_t()})) {\n              return QBDI::VMAction::BREAK_TO_VM;\n            }\n          }\n        }\n        return QBDI::VMAction::CONTINUE;\n      });\n\n  bool ran = vm.run(start, FAKE_RET_ADDR);\n  REQUIRE(ran);\n\n  QBDI::rword ret = QBDI_GPR_GET(state, QBDI::REG_RETURN);\n  REQUIRE(ret == (QBDI::rword)42);\n\n  SUCCEED();\n}\n\nstruct CheckReduceSizeData {\n  size_t lastCount;\n};\n\nTEST_CASE_METHOD(APITest, \"VMTest-ReduceSize1\") {\n  uint32_t count = 0;\n  // add dummy callback in order to increase the size of each patch\n  vm.addCodeCB(QBDI::InstPosition::PREINST, countInstruction, &count);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, countInstruction, &count);\n\n  CheckReduceSizeData data = {0};\n  vm.addVMEventCB(QBDI::SEQUENCE_ENTRY | QBDI::SEQUENCE_EXIT |\n                      QBDI::BASIC_BLOCK_NEW,\n                  [&](QBDI::VM *evm, const QBDI::VMState *vmState,\n                      QBDI::GPRState *gprState, QBDI::FPRState *fprState) {\n                    CHECK(evm == &vm);\n                    if ((vmState->event & QBDI::BASIC_BLOCK_NEW) != 0) {\n                      data.lastCount = vm.getNbExecBlock();\n                    }\n                    CHECK(data.lastCount == vm.getNbExecBlock());\n                    return QBDI::VMAction::CONTINUE;\n                  });\n\n  CHECK(vm.getNbExecBlock() == 0);\n\n  // backup GPRState to have the same state before each run\n  QBDI::GPRState backup = *(vm.getGPRState());\n\n  for (QBDI::rword j = 0; j < 4; j++) {\n    for (QBDI::rword i = 0; i < 8; i++) {\n      QBDI_DEBUG(\"Begin Loop iteration {} {}\", j, i);\n      vm.setGPRState(&backup);\n\n      QBDI::rword retval;\n      bool ran =\n          vm.call(&retval, reinterpret_cast<QBDI::rword>(dummyFunBB),\n                  {i ^ j, 5, 13, reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1)});\n      CHECK(ran);\n      CHECK(data.lastCount != 0);\n    }\n  }\n\n  size_t currentExecBlock = vm.getNbExecBlock();\n  while (currentExecBlock != 0) {\n    vm.reduceCacheTo(currentExecBlock - 1);\n    size_t newExecBlock = vm.getNbExecBlock();\n    CHECK(newExecBlock < currentExecBlock);\n    currentExecBlock = newExecBlock;\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-ReduceSize2\") {\n  uint32_t count = 0;\n  // add dummy callback in order to increase the size of each patch\n  vm.addCodeCB(QBDI::InstPosition::PREINST, countInstruction, &count);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, countInstruction, &count);\n\n  vm.addVMEventCB(QBDI::BASIC_BLOCK_NEW,\n                  [](QBDI::VM *vm, const QBDI::VMState *vmState,\n                     QBDI::GPRState *gprState, QBDI::FPRState *fprState) {\n                    size_t lastCount = vm->getNbExecBlock();\n                    vm->reduceCacheTo(0);\n                    // vm doesn't clear the cache immediatly\n                    CHECK(lastCount == vm->getNbExecBlock());\n                    return QBDI::VMAction::CONTINUE;\n                  });\n\n  CHECK(vm.getNbExecBlock() == 0);\n\n  // backup GPRState to have the same state before each run\n  QBDI::GPRState backup = *(vm.getGPRState());\n\n  for (QBDI::rword j = 0; j < 4; j++) {\n    for (QBDI::rword i = 0; i < 8; i++) {\n      QBDI_DEBUG(\"Begin Loop iteration {} {}\", j, i);\n      vm.setGPRState(&backup);\n\n      QBDI::rword retval;\n      bool ran =\n          vm.call(&retval, reinterpret_cast<QBDI::rword>(dummyFunBB),\n                  {i ^ j, 5, 13, reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1)});\n      CHECK(ran);\n      CHECK(vm.getNbExecBlock() == 0);\n    }\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"VMTest-JitAnalysis\") {\n  uint32_t count = 0;\n  // add dummy callback in order to increase the size of each patch\n  vm.addCodeCB(QBDI::InstPosition::PREINST, countInstruction, &count);\n  vm.addCodeCB(QBDI::InstPosition::POSTINST, countInstruction, &count);\n\n  std::set<QBDI::rword> instAddress;\n\n  vm.addInstrRule(\n      [&](QBDI::VM *evm, const QBDI::InstAnalysis *inst)\n          -> std::vector<QBDI::InstrRuleDataCBK> {\n        CHECK(evm == &vm);\n#ifdef QBDI_ARCH_ARM\n        if (inst->cpuMode == QBDI::CPUMode::Thumb) {\n          instAddress.insert(inst->address | 1);\n        } else {\n          instAddress.insert(inst->address);\n        }\n#else\n        instAddress.insert(inst->address);\n#endif\n        return {};\n      },\n      QBDI::AnalysisType::ANALYSIS_INSTRUCTION);\n\n  // backup GPRState to have the same state before each run\n  QBDI::GPRState backup = *(vm.getGPRState());\n\n  for (QBDI::rword j = 0; j < 4; j++) {\n    for (QBDI::rword i = 0; i < 8; i++) {\n      QBDI_DEBUG(\"Begin Loop iteration {} {}\", j, i);\n      vm.setGPRState(&backup);\n\n      QBDI::rword retval;\n      bool ran =\n          vm.call(&retval, reinterpret_cast<QBDI::rword>(dummyFunBB),\n                  {i ^ j, 5, 13, reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1),\n                   reinterpret_cast<QBDI::rword>(dummyFun1)});\n      CHECK(ran);\n    }\n  }\n\n  for (auto address : instAddress) {\n    const auto *ana = vm.getCachedInstAnalysis(\n        address, QBDI::AnalysisType::ANALYSIS_JIT |\n                     QBDI::AnalysisType::ANALYSIS_INSTRUCTION);\n    REQUIRE(ana != nullptr);\n    CHECK(ana->patchAddress != 0);\n    CHECK(((uint32_t)ana->patchInstOffset) + ((uint32_t)ana->patchInstSize) <=\n          ((uint32_t)ana->patchSize));\n\n    const size_t increment = (QBDI::is_arm || QBDI::is_aarch64) ? 4 : 1;\n\n    for (size_t i = 0; i < ana->patchSize; i += increment) {\n      const auto *anaJit = vm.getJITInstAnalysis(\n          ana->patchAddress + i, QBDI::AnalysisType::ANALYSIS_JIT |\n                                     QBDI::AnalysisType::ANALYSIS_INSTRUCTION);\n      CHECK(anaJit != nullptr);\n      if (anaJit != nullptr) {\n        CHECK(anaJit->address == ana->address);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/API/X86/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysisTest_X86.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest_X86.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/OptionsTest_X86.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/VMTest_X86.cpp\")\n"
  },
  {
    "path": "test/API/X86/InstAnalysisTest_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n\nstruct ExpectedInstAnalysis {\n  std::string mnemonic;\n  QBDI::rword address;\n  uint32_t instSize;\n  bool affectControlFlow;\n  bool isBranch;\n  bool isCall;\n  bool isReturn;\n  bool isCompare;\n  bool isPredicable;\n  bool mayLoad;\n  bool mayStore;\n  uint32_t loadSize;\n  uint32_t storeSize;\n  QBDI::ConditionType condition;\n};\n\n[[maybe_unused]] static void debugOperand(const QBDI::InstAnalysis *ana) {\n  if ((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n      QBDI::ANALYSIS_OPERANDS) {\n    for (int i = 0; i < ana->numOperands; i++) {\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      WARN(\"- type: \" << op.type << \", flag: \" << op.flag\n                      << \", value: \" << op.value << \", size: \" << (int)op.size\n                      << \", regOff : \" << (int)op.regOff\n                      << \", regCtxIdx: \" << op.regCtxIdx << \", regName: \"\n                      << (op.regName == nullptr ? \"nullptr\" : op.regName)\n                      << \", regAccess: \"\n                      << (op.regAccess & QBDI::REGISTER_READ ? \"r\" : \"-\")\n                      << (op.regAccess & QBDI::REGISTER_WRITE ? \"w\" : \"-\"));\n    }\n  }\n}\n\nstatic void checkOperand(const QBDI::InstAnalysis *ana,\n                         const std::vector<QBDI::OperandAnalysis> expecteds,\n                         QBDI::RegisterAccessType flagsAccess) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n             QBDI::ANALYSIS_OPERANDS) {\n    CHECK(flagsAccess == ana->flagsAccess);\n    CHECK(expecteds.size() == ana->numOperands);\n    for (unsigned i = 0;\n         i < std::min<unsigned>(ana->numOperands, expecteds.size()); i++) {\n      const QBDI::OperandAnalysis &expect = expecteds[i];\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      INFO(\"For operand \" << i);\n\n      CHECK(expect.type == op.type);\n      CHECK(expect.flag == op.flag);\n      if (op.type == QBDI::OPERAND_IMM || expect.value != 0) {\n        CHECK(expect.value == op.value);\n      }\n      CHECK(expect.size == op.size);\n      CHECK(expect.regOff == op.regOff);\n      CHECK(expect.regCtxIdx == op.regCtxIdx);\n      CHECK(expect.regAccess == op.regAccess);\n\n      const std::string expectedRegName(\n          (expect.regName != nullptr) ? expect.regName : \"\");\n      const std::string foundRegName((op.regName != nullptr) ? op.regName : \"\");\n      CHECK(expectedRegName == foundRegName);\n\n      if (expect.regName == nullptr || op.regName == nullptr) {\n        CHECK(expect.regName == op.regName);\n      }\n    }\n  }\n}\n\nstatic void checkInst(const QBDI::InstAnalysis *ana,\n                      const ExpectedInstAnalysis expected) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_INSTRUCTION) ==\n             QBDI::ANALYSIS_INSTRUCTION) {\n    CHECK(expected.mnemonic == ana->mnemonic);\n    CHECK(expected.address == ana->address);\n    CHECK(expected.instSize == ana->instSize);\n    CHECK(expected.affectControlFlow == ana->affectControlFlow);\n    CHECK(expected.isBranch == ana->isBranch);\n    CHECK(expected.isCall == ana->isCall);\n    CHECK(expected.isReturn == ana->isReturn);\n    CHECK(expected.isCompare == ana->isCompare);\n    CHECK(expected.isPredicable == ana->isPredicable);\n    CHECK(expected.mayLoad == ana->mayLoad);\n    CHECK(expected.mayStore == ana->mayStore);\n    CHECK(expected.loadSize == ana->loadSize);\n    CHECK(expected.storeSize == ana->storeSize);\n    CHECK(expected.condition == ana->condition);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-CachedInst\") {\n\n  QBDI::rword addr = genASM(\"leal (%eax), %ebx\\n\");\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n\n  vm.clearAllCache();\n\n  CHECK(vm.getCachedInstAnalysis(addr) == nullptr);\n\n  vm.precacheBasicBlock(addr);\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-lea\") {\n\n  QBDI::rword addr = genASM(\"leal (%eax), %ebx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LEA32r\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-lea-same-reg\") {\n\n  QBDI::rword addr = genASM(\"leal (%eax,%eax), %eax\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LEA32r\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movrm\") {\n\n  QBDI::rword addr = genASM(\"movl 0x45(%eax,%edx,4), %ebx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV32rm\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 3,\n                    \"EDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x45, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movrm-seg\") {\n\n  QBDI::rword addr = genASM(\"movl %gs:0x45(%eax,%edx,4), %ebx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV32rm\", addr,\n          /* instSize */ 5, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 3,\n                    \"EDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x45, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_ADDR, 0, 2, 0, -1,\n                    \"GS\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-addmi\") {\n\n  QBDI::rword addr = genASM(\"addl\t$0x4157, (%ecx)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADD32mi\", addr,\n          /* instSize */ 6, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 4, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, 0x4157, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movrr\") {\n\n  QBDI::rword addr = genASM(\"mov %ecx, %ebx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV32rr\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movrr8\") {\n\n  QBDI::rword addr = genASM(\"mov %ch, %bl\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV8rr\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 1, 0, 1, \"BL\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 1, 8, 2, \"CH\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-xchgrr\") {\n\n  QBDI::rword addr = genASM(\"xchg %ecx, %ebx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"XCHG32rr\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-addrr\") {\n\n  QBDI::rword addr = genASM(\"add %ecx, %ebx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADD32rr\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movoa\") {\n\n  QBDI::rword addr = genASM(\"mov %fs:0x0, %eax\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV32ao32\", addr,\n          /* instSize */ 6, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_ADDR, 0, 2, 0, -1,\n                    \"FS\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movsb\") {\n\n  QBDI::rword addr = genASM(\"movsb\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOVSB\", addr,\n          /* instSize */ 1, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 1, /* storeSize */ 1,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 5,\n                    \"EDI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 4,\n                    \"ESI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 5,\n                    \"EDI\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 4,\n                    \"ESI\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-cmpsb\") {\n\n  QBDI::rword addr = genASM(\"cmpsb\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMPSB\", addr,\n          /* instSize */ 1, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 5,\n                    \"EDI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 4,\n                    \"ESI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 5,\n                    \"EDI\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 4,\n                    \"ESI\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_READ_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-cmpmr\") {\n\n  QBDI::rword addr = genASM(\"cmpl %ecx, (%eax,%edx)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMP32mr\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 3,\n                    \"EDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-cmprm\") {\n\n  QBDI::rword addr = genASM(\"cmpl (%eax,%edx), %ecx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMP32rm\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 3,\n                    \"EDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-ret\") {\n\n  QBDI::rword addr = genASM(\"retl\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"RET32\", addr,\n          /* instSize */ 1, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ true, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 7,\n                    \"ESP\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-call\") {\n\n  QBDI::rword addr = genASM(\"call test_custom_call\\ntest_custom_call:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CALLpcrel32\", addr,\n          /* instSize */ 5, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 7,\n                    \"ESP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, -1,\n                    \"SSP\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-callr\") {\n\n  QBDI::rword addr = genASM(\"calll *%eax\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CALL32r\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 7,\n                    \"ESP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, -1,\n                    \"SSP\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-callm\") {\n\n  QBDI::rword addr = genASM(\"calll *0xa(%eax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CALL32m\", addr,\n          /* instSize */ 3, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 4, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0xa, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 7,\n                    \"ESP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, -1,\n                    \"SSP\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\n#define NOP8 \"nop\\nnop\\nnop\\nnop\\nnop\\nnop\\nnop\\nnop\\n\"\n#define NOP64 NOP8 NOP8 NOP8 NOP8 NOP8 NOP8 NOP8 NOP8\n#define NOP512 NOP64 NOP64 NOP64 NOP64 NOP64 NOP64 NOP64 NOP64\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-jmpi1\") {\n\n  QBDI::rword addr = genASM(\"jmp test_jmp\\n\" NOP8 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JMP_1\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 8, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-jmpi4\") {\n\n  QBDI::rword addr = genASM(\"jmp test_jmp\\n\" NOP512 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JMP_4\", addr,\n          /* instSize */ 5, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 512, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-je1\") {\n\n  QBDI::rword addr = genASM(\"je test_jmp\\n\" NOP8 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JCC_1\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 8, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-je4\") {\n\n  QBDI::rword addr = genASM(\"je test_jmp\\n\" NOP512 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JCC_4\", addr,\n          /* instSize */ 6, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 512, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-jmpm\") {\n\n  QBDI::rword addr = genASM(\"jmpl *0xa(%eax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JMP32m\", addr,\n          /* instSize */ 3, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 4, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0xa, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-fldl\") {\n\n  QBDI::rword addr = genASM(\"fldl (%eax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LD_F64m\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 0,\n                    \"FPCW\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 2,\n                    \"FPSW\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-fstps\") {\n\n  QBDI::rword addr = genASM(\"fstps (%eax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ST_FP32m\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 0,\n                    \"FPCW\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 2,\n                    \"FPSW\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movapd\") {\n\n  QBDI::rword addr = genASM(\"movapd (%eax), %xmm1\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOVAPDrm\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 16, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n      {\n          {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0,\n           offsetof(QBDI::FPRState, xmm1), \"XMM1\", QBDI::REGISTER_WRITE},\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0, \"EAX\",\n           QBDI::REGISTER_READ},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x0, 4, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n      },\n      QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-paddb\") {\n\n  QBDI::rword addr = genASM(\"paddb %mm1, %mm0\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MMX_PADDBrr\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 32,\n                    \"MM0\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 48,\n                    \"MM1\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-vpaddb\") {\n\n  QBDI::rword addr = genASM(\"vpaddb %xmm2, %xmm1, %xmm0\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VPADDBrr\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 160,\n                    \"XMM0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 176,\n                    \"XMM1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 192,\n                    \"XMM2\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-xlatb\") {\n\n  QBDI::rword addr = genASM(\"xlatb\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"XLAT\", addr,\n          /* instSize */ 1, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 1, 0, 0,\n                    \"AL\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-movdir64b\") {\n\n  QBDI::rword addr = genASM(\"movdir64b 0xc(%eax), %ecx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOVDIR64B32\", addr,\n          /* instSize */ 6, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 512, /* storeSize */ 512,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0xc, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-loop\") {\n\n  QBDI::rword addr = genASM(\"target:\\n    loop target\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LOOP\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, -2, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-loope\") {\n\n  QBDI::rword addr = genASM(\"target:\\n    loope target\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LOOPE\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, -2, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86-loopne\") {\n\n  QBDI::rword addr = genASM(\"target:\\n    loopne target\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LOOPNE\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NOT_EQUALS});\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, -2, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n"
  },
  {
    "path": "test/API/X86/MemoryAccessTest_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#include \"Utility/System.h\"\n\nstatic bool checkFeature(const char *f) {\n  if (!QBDI::isHostCPUFeaturePresent(f)) {\n    WARN(\"Host doesn't support \" << f << \" feature: SKIP\");\n    return false;\n  }\n  return true;\n}\n\n/*\nstatic QBDI::VMAction debugCB(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\nQBDI::FPRState *fprState, void *data) { const QBDI::InstAnalysis* instAnalysis =\nvm->getInstAnalysis(); printf(\"0x%x (%10s): %s\\n\", instAnalysis->address,\ninstAnalysis->mnemonic, instAnalysis->disassembly);\n\n    for (auto &a: vm->getInstMemoryAccess()) {\n        printf(\" - inst: 0x%x, addr: 0x%x, size: %d, type: %c%c, value: 0x%x,\nflags: 0x%x\\n\", a.instAddress, a.accessAddress, a.size,\n            ((a.type & QBDI::MEMORY_READ) !=0 )?'r':'-',\n            ((a.type & QBDI::MEMORY_WRITE) !=0 )?'w':'-',\n            a.value, a.flags);\n    }\n    return QBDI::VMAction::CONTINUE;\n}// */\n\nstruct ExpectedMemoryAccess {\n  QBDI::rword address;\n  QBDI::rword value;\n  uint16_t size;\n  QBDI::MemoryAccessType type;\n  QBDI::MemoryAccessFlags flags;\n  bool see = false;\n};\n\nstruct ExpectedMemoryAccesses {\n  std::vector<ExpectedMemoryAccess> accesses;\n};\n\nstatic QBDI::VMAction checkAccess(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n\n  ExpectedMemoryAccesses *info = static_cast<ExpectedMemoryAccesses *>(data);\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n\n  if (std::all_of(info->accesses.begin(), info->accesses.end(),\n                  [](ExpectedMemoryAccess &a) { return a.see; }))\n    return QBDI::VMAction::CONTINUE;\n\n  CHECKED_IF(memaccesses.size() == info->accesses.size()) {\n    for (size_t i = 0; i < info->accesses.size(); i++) {\n      auto &memaccess = memaccesses[i];\n      auto &expect = info->accesses[i];\n      CHECKED_IF(memaccess.accessAddress == expect.address)\n      CHECKED_IF((memaccess.value == expect.value || expect.value == 0))\n      CHECKED_IF(memaccess.size == expect.size)\n      CHECKED_IF(memaccess.type == expect.type)\n      CHECKED_IF(memaccess.flags == expect.flags)\n      expect.see = true;\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\n// test stack memory access\n// PUSH POP CALL RET\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-push_pop_reg\") {\n\n  const char source[] =\n      \"xchg %esp, %ebx\\n\"\n      \"push %eax\\n\"\n      \"pop %eax\\n\"\n      \"xchg %esp, %ebx\\n\";\n\n  QBDI::rword v1 = 0x6bef61ae;\n  QBDI::rword tmpStack[10] = {0};\n  ExpectedMemoryAccesses expectedPush = {{\n      {(QBDI::rword)&tmpStack[8], v1, 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPop = {{\n      {(QBDI::rword)&tmpStack[8], v1, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"PUSH32r\", QBDI::POSTINST, checkAccess, &expectedPush);\n  vm.addMnemonicCB(\"POP32r\", QBDI::PREINST, checkAccess, &expectedPop);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v1;\n  state->ebx = (QBDI::rword)&tmpStack[9];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPop.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPush.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-pusha_popa\") {\n\n  const char source[] =\n      \"xchg %esp, %ebx\\n\"\n      \"pusha\\n\"\n      \"popa\\n\"\n      \"xchg %esp, %ebx\\n\";\n\n  QBDI::rword tmpStack[10] = {0};\n  ExpectedMemoryAccesses expectedPusha = {{\n      {(QBDI::rword)&tmpStack[1], 0, 4 * 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPopa = {{\n      {(QBDI::rword)&tmpStack[1], 0, 4 * 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"PUSHA32\", QBDI::POSTINST, checkAccess, &expectedPusha);\n  vm.addMnemonicCB(\"POPA32\", QBDI::PREINST, checkAccess, &expectedPopa);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->ebx = (QBDI::rword)&tmpStack[9];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPopa.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPusha.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-push_pop_mem\") {\n\n  const char source[] =\n      \"xchg %esp, %ebx\\n\"\n      \"push (%eax)\\n\"\n      \"pop (%eax)\\n\"\n      \"xchg %esp, %ebx\\n\";\n\n  QBDI::rword v1 = 0xab367201;\n  QBDI::rword tmpStack[10] = {0};\n  ExpectedMemoryAccesses expectedPushPre = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPushPost = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&tmpStack[8], v1, 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPopPre = {{\n      {(QBDI::rword)&tmpStack[8], v1, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPopPost = {{\n      {(QBDI::rword)&tmpStack[8], v1, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"PUSH32rmm\", QBDI::PREINST, checkAccess, &expectedPushPre);\n  vm.addMnemonicCB(\"PUSH32rmm\", QBDI::POSTINST, checkAccess, &expectedPushPost);\n  vm.addMnemonicCB(\"POP32rmm\", QBDI::PREINST, checkAccess, &expectedPopPre);\n  vm.addMnemonicCB(\"POP32rmm\", QBDI::POSTINST, checkAccess, &expectedPopPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = (QBDI::rword)&v1;\n  state->ebx = (QBDI::rword)&tmpStack[9];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPopPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPopPost.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPushPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPushPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-call_ret\") {\n\n  const char source[] =\n      \"    xchg %esp, %ebx\\n\"\n      \"    call test_call_ret_custom_call\\n\"\n      \"    jmp test_call_ret_custom_end\\n\"\n      \"test_call_ret_custom_call:\\n\"\n      \"    ret\\n\"\n      \"test_call_ret_custom_end:\\n\"\n      \"    xchg %esp, %ebx\\n\";\n\n  QBDI::rword tmpStack[10] = {0};\n  ExpectedMemoryAccesses expectedCall = {{\n      {(QBDI::rword)&tmpStack[8], 0, 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedRet = {{\n      {(QBDI::rword)&tmpStack[8], 0, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"CALL*\", QBDI::POSTINST, checkAccess, &expectedCall);\n  vm.addMnemonicCB(\"RET*\", QBDI::PREINST, checkAccess, &expectedRet);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->ebx = (QBDI::rword)&tmpStack[9];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedCall.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedRet.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movao\") {\n\n  const char source[] = \"mov %gs:0x0, %eax\\n\";\n\n  ExpectedMemoryAccesses expected = {{\n      {0, 0, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"MOV32ao32\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\n// COMPS MOVS SCAS LODS STOS\n// REP and REPNE prefix\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-cmpsb\") {\n\n  const char source[] = \"cmpsb\\n\";\n\n  QBDI::rword v1 = 0xaa, v2 = 0x55;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v2, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-cmpsw\") {\n\n  const char source[] = \"cmpsw\\n\";\n\n  QBDI::rword v1 = 0x783, v2 = 0xbd7a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v2, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-cmpsd\") {\n\n  const char source[] = \"cmpsl\\n\";\n\n  QBDI::rword v1 = 0x6ef9efbd, v2 = 0xef783b2a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v2, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSL\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-rep_cmpsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"rep cmpsb\\n\";\n\n  uint8_t v1[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6d};\n  uint8_t v2[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6c};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  state->ecx = sizeof(v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-repne_cmpsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"repne cmpsb\\n\";\n\n  uint8_t v1[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6d};\n  uint8_t v2[10] = {0xb1, 0x5, 0x98, 0xae, 0xe2, 0xe6, 0x19, 0xf9, 0xc7, 0x6d};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  state->ecx = sizeof(v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-rep_cmpsb2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"rep cmpsb\\n\"\n      \"cld\\n\";\n\n  uint8_t v1[10] = {0x5c, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6c};\n  uint8_t v2[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6c};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[9], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2[9], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1[9];\n  state->edi = (QBDI::rword)&v2[9];\n  state->ecx = sizeof(v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-rep_cmpsw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"rep cmpsw\\n\";\n\n  uint16_t v1[5] = {0x5c78, 0x89ab, 0xe6e7, 0x1afa, 0xc86c};\n  uint16_t v2[5] = {0x5c78, 0x89ab, 0xe6e7, 0x1afa, 0xc86d};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  state->ecx = sizeof(v1) / sizeof(uint16_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-rep_cmpsw2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"rep cmpsw\\n\"\n      \"cld\\n\";\n\n  uint16_t v1[5] = {0x5c78, 0x89ab, 0xe6e7, 0x1afa, 0xc86c};\n  uint16_t v2[5] = {0x5678, 0x89ab, 0xe6e7, 0x1afa, 0xc86c};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[4], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2[4], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1[4];\n  state->edi = (QBDI::rword)&v2[4];\n  state->ecx = sizeof(v1) / sizeof(uint16_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"movsb\\n\";\n\n  QBDI::rword v1 = 0xbf, v2 = 0x78;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movsw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"movsw\\n\";\n\n  QBDI::rword v1 = 0x789f, v2 = 0xbd67;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movsl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"movsl\\n\";\n\n  QBDI::rword v1 = 0xa579eb9d, v2 = 0x2389befa;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movsb2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"movsb\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x7f;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movsw2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"movsw\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0xad63, v2 = 0x6219;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movsl2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"movsl\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0xefa036db, v2 = 0xefd7137a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-rep_movsl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"rep movsl\\n\";\n\n  uint32_t v1[5] = {0xab673, 0xeba9256, 0x638feba8, 0x7182faB, 0x7839021b};\n  uint32_t v2[5] = {0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, sizeof(v1), QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, sizeof(v1), QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  state->edi = (QBDI::rword)&v2;\n  state->ecx = sizeof(v1) / sizeof(uint32_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (size_t i = 0; i < sizeof(v1) / sizeof(uint32_t); i++)\n    CHECK(v2[i] == v1[i]);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-rep_movsl2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"rep movsl\\n\"\n      \"cld\\n\";\n\n  uint32_t v1[5] = {0xab673, 0xeba9256, 0x638feba8, 0x7182faB, 0x7839021b};\n  uint32_t v2[5] = {0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[4], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, sizeof(v1), QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, sizeof(v1), QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1[4];\n  state->edi = (QBDI::rword)&v2[4];\n  state->ecx = sizeof(v1) / sizeof(uint32_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (size_t i = 0; i < sizeof(v1) / sizeof(uint32_t); i++)\n    CHECK(v2[i] == v1[i]);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-scasb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"scasb\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v2, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SCASB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-scasw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"scasw\\n\";\n\n  QBDI::rword v1 = 0x5ef1, v2 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v2, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SCASW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-scasl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"scasl\\n\";\n\n  QBDI::rword v1 = 0x629ebf, v2 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v2, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SCASL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-lodsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"lodsb\\n\";\n\n  QBDI::rword v1 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LODSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(retval == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-lodsw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"lodsw\\n\";\n\n  QBDI::rword v1 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LODSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(retval == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-lodsl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"lodsl\\n\";\n\n  QBDI::rword v1 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LODSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->esi = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(retval == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-stosb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"stosb\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-stosw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"stosw\\n\";\n\n  QBDI::rword v1 = 0x5ef1, v2 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-stosl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"stosl\\n\";\n\n  QBDI::rword v1 = 0x629ebf, v2 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-stosb2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"stosb\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-stosw2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"stosw\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x5ef1, v2 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-stosl2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"stosl\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x629ebf, v2 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v1;\n  state->edi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movzx\") {\n\n  const char source[] = \"movzbl  0x5(%ebx), %eax\\n\";\n\n  uint8_t v[] = {0xeb, 0xaf, 0x71, 0x96, 0x30, 0x14, 0x52, 0xce};\n\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v[5], v[5], 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVZX32rm8\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = 0xfab792eb;\n  state->ebx = (QBDI::rword)&v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(state->eax == v[5]);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-addmr\") {\n\n  const char source[] = \"addl %eax, (%ebx)\";\n\n  const uint32_t v1 = 0xebaf7196;\n  const uint32_t v2 = 0xfab792eb;\n  uint32_t buff = v1;\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&buff, v1 + v2, 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"ADD32mr\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"ADD32mr\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = v2;\n  state->ebx = (QBDI::rword)&buff;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(buff == v1 + v2);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-xchgrm\") {\n\n  const char source[] = \"xchgl %eax, (%eax)\";\n\n  const uint32_t v1 = 0x96761ef1;\n  uint32_t buff = v1;\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&buff, (QBDI::rword)&buff, 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"XCHG32rm\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"XCHG32rm\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = (QBDI::rword)&buff;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(state->eax == v1);\n  CHECK(buff == (uint32_t)&buff);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-enter-leave\") {\n\n  const char source[] =\n      \"xchg %esp, %ebx\\n\"\n      \"enter $0x0, $0x0\\n\"\n      \"leave\\n\"\n      \"xchg %esp, %ebx\\n\";\n\n  const uint64_t v = 0x819abe76;\n  QBDI::rword tmpStack[10] = {0};\n\n  ExpectedMemoryAccesses expectedEnter = {{\n      {(QBDI::rword)&tmpStack[8], v, 4, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedLeave = {{\n      {(QBDI::rword)&tmpStack[8], v, 4, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"ENTER\", QBDI::POSTINST, checkAccess, &expectedEnter);\n  vm.addMnemonicCB(\"LEAVE*\", QBDI::PREINST, checkAccess, &expectedLeave);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->ebx = (QBDI::rword)&tmpStack[9];\n  state->ebp = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(state->ebp == v);\n  CHECK(state->ebx == (QBDI::rword)&tmpStack[9]);\n  for (auto &e : expectedEnter.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedLeave.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-fld-fstp\") {\n\n  if (!checkFeature(\"mmx\")) {\n    return;\n  }\n\n  const char source[] =\n      \"flds\t(%eax)\\n\"\n      \"fldl (%ebx)\\n\"\n      \"movl\t$0x0, (%eax)\\n\"\n      \"movl\t$0x0, (%ebx)\\n\"\n      \"fstpl (%ebx)\\n\"\n      \"fstps (%eax)\\n\";\n\n  const uint32_t v1 = 0x416ac41e;\n  const uint64_t v2 = 0x79819abe76;\n  uint32_t buff1 = v1;\n  uint64_t buff2 = v2;\n\n  ExpectedMemoryAccesses expectedLoad32 = {{\n      {(QBDI::rword)&buff1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedLoad64 = {{\n      {(QBDI::rword)&buff2, 0, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedStore64 = {{\n      {(QBDI::rword)&buff2, 0, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedStore32 = {{\n      {(QBDI::rword)&buff1, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD_F32m\", QBDI::PREINST, checkAccess, &expectedLoad32);\n  vm.addMnemonicCB(\"LD_F64m\", QBDI::PREINST, checkAccess, &expectedLoad64);\n  vm.addMnemonicCB(\"ST_FP64m\", QBDI::POSTINST, checkAccess, &expectedStore64);\n  vm.addMnemonicCB(\"ST_FP32m\", QBDI::POSTINST, checkAccess, &expectedStore32);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = (QBDI::rword)&buff1;\n  state->ebx = (QBDI::rword)&buff2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(buff1 == v1);\n  CHECK(buff2 == v2);\n  for (auto &e : expectedLoad32.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedLoad64.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedStore64.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedStore32.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movapd\") {\n\n  if (!checkFeature(\"sse2\")) {\n    return;\n  }\n\n  const char source[] =\n      \"movapd\t(%eax), %xmm1\\n\"\n      \"movapd %xmm2, (%ebx)\\n\";\n\n  const uint8_t v1[16] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27,\n                          0x67, 0x4f, 0x91, 0x6e, 0x4b, 0x57, 0x4d, 0xc9};\n  const uint8_t v2[16] = {0xa9, 0x5d, 0x27, 0x6a, 0xc4, 0x91, 0x6e, 0x4b,\n                          0x57, 0x4d, 0x41, 0x6a, 0x0e, 0x80, 0xeb, 0xad};\n  QBDI_ALIGNED(16) uint8_t buff1[16];\n  QBDI_ALIGNED(16) uint8_t buff2[16] = {0};\n\n  memcpy(buff1, v1, sizeof(v1));\n\n  ExpectedMemoryAccesses expectedLoad = {{\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedStore = {{\n      {(QBDI::rword)&buff2, 0, 16, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVAPDrm\", QBDI::PREINST, checkAccess, &expectedLoad);\n  vm.addMnemonicCB(\"MOVAPDmr\", QBDI::POSTINST, checkAccess, &expectedStore);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = (QBDI::rword)&buff1;\n  state->ebx = (QBDI::rword)&buff2;\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  memset(fstate->xmm1, '\\x00', sizeof(v1));\n  memcpy(fstate->xmm2, v2, sizeof(v2));\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(memcmp(fstate->xmm2, buff2, sizeof(v2)) == 0);\n  CHECK(memcmp(fstate->xmm1, v1, sizeof(v1)) == 0);\n  for (auto &e : expectedLoad.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedStore.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-maskmovdqu\") {\n\n  if (!checkFeature(\"avx\")) {\n    return;\n  }\n\n  const char source[] = \"maskmovdqu\t%xmm1, %xmm0\\n\";\n\n  const uint8_t v1[16] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27,\n                          0x67, 0x4f, 0x91, 0x6e, 0x4b, 0x57, 0x4d, 0xc9};\n  const uint8_t v2[16] = {0xa9, 0x5d, 0x27, 0x6a, 0xc4, 0x91, 0x6e, 0x4b,\n                          0x57, 0x4d, 0x41, 0x6a, 0x0e, 0x80, 0xeb, 0xad};\n  const uint8_t mask[16] = {0x80, 0x80, 0x80, 0x80, 0x0, 0x80, 0x0,  0x80,\n                            0x80, 0x0,  0x80, 0x0,  0x0, 0x80, 0x80, 0x0};\n  QBDI_ALIGNED(16) uint8_t buff1[16];\n\n  memcpy(buff1, v1, sizeof(v1));\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MASKMOVDQU\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MASKMOVDQU\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->edi = (QBDI::rword)&buff1;\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  memcpy(fstate->xmm0, v2, sizeof(v2));\n  memcpy(fstate->xmm1, mask, sizeof(mask));\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (unsigned i = 0; i < sizeof(buff1); i++)\n    CHECK(((mask[i] == 0) ? v1[i] : v2[i]) == buff1[i]);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-maskmovq\") {\n\n  if (!checkFeature(\"avx\") || !checkFeature(\"sse\")) {\n    return;\n  }\n\n  const char source[] = \"maskmovq\t%mm1, %mm0\\n\";\n\n  const uint8_t v1[8] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27};\n  const uint8_t v2[8] = {0xa9, 0x5d, 0x27, 0x6a, 0xc4, 0x91, 0x6e, 0x4b};\n  const uint8_t mask[8] = {0x80, 0x80, 0x80, 0x80, 0x0, 0x80, 0x0, 0x80};\n  QBDI_ALIGNED(8) uint8_t buff1[8];\n\n  memcpy(buff1, v1, sizeof(v1));\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff1, 0, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff1, 0, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&buff1, 0, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MMX_MASKMOVQ\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MMX_MASKMOVQ\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->edi = (QBDI::rword)&buff1;\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  memcpy(fstate->stmm0.reg, v2, sizeof(v2));\n  memcpy(fstate->stmm1.reg, mask, sizeof(mask));\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (unsigned i = 0; i < sizeof(buff1); i++)\n    CHECK(((mask[i] == 0) ? v1[i] : v2[i]) == buff1[i]);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-xlat\") {\n\n  const char source[] = \"xlatb\\n\";\n\n  const uint8_t v[8] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27};\n\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v[5], v[5], 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"XLAT\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->ebx = (QBDI::rword)&v;\n  state->eax = 5;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v[5] == vm.getGPRState()->eax);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-movdir64b\") {\n\n  if (!checkFeature(\"movdir64b\")) {\n    return;\n  }\n\n  const char source[] = \"movdir64b (%eax), %ecx\\n\";\n\n  uint8_t v[512] = {0};\n  uint8_t buff[512] = {0};\n\n  for (size_t i = 0; i < sizeof(v); i++) {\n    v[i] = (i % 256);\n  }\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, 0, 512, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v, 0, 512, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&buff, 0, 512, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVDIR64B32\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVDIR64B32\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = (QBDI::rword)&v;\n  state->ecx = (QBDI::rword)&buff;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (size_t i = 0; i < sizeof(v); i++)\n    CHECK(buff[i] == (i % 256));\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86-xsave\") {\n\n  if (!checkFeature(\"xsave\")) {\n    return;\n  }\n\n  const char source[] = \"xsave (%ecx)\\n\";\n\n  QBDI_ALIGNED(64) uint8_t v[4096] = {0};\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, 0, 576, QBDI::MEMORY_READ,\n       QBDI::MEMORY_MINIMUM_SIZE | QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v, 0, 576, QBDI::MEMORY_READ,\n       QBDI::MEMORY_MINIMUM_SIZE | QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v, 0, 576, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_MINIMUM_SIZE | QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"XSAVE\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"XSAVE\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->eax = 7;\n  state->edx = 0;\n  state->ecx = (QBDI::rword)&v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n"
  },
  {
    "path": "test/API/X86/OptionsTest_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/OptionsTest.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n\nTEST_CASE_METHOD(OptionsTest, \"OptionsTest_X86-ATTSyntax\") {\n\n  InMemoryObject leaObj(\"leal (%eax), %ebx\\nret\\n\");\n  QBDI::rword addr = (QBDI::rword)leaObj.getCode().data();\n\n  vm.setOptions(QBDI::Options::NO_OPT);\n  CHECK(vm.precacheBasicBlock(addr));\n\n  const QBDI::InstAnalysis *ana =\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY);\n  REQUIRE(ana != nullptr);\n  REQUIRE(ana->disassembly != nullptr);\n\n  CHECK(std::string(ana->disassembly) == std::string(\"\\tlea\\tebx, [eax]\"));\n\n  vm.clearAllCache();\n  vm.setOptions(QBDI::Options::OPT_ATT_SYNTAX);\n  CHECK(vm.precacheBasicBlock(addr));\n\n  ana = vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY);\n  REQUIRE(ana != nullptr);\n  REQUIRE(ana->disassembly != nullptr);\n\n  CHECK(std::string(ana->disassembly) == std::string(\"\\tleal\\t(%eax), %ebx\"));\n}\n\nstatic QBDI::VMAction setBool(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                              QBDI::FPRState *fprState, void *data) {\n  *((bool *)data) = true;\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(OptionsTest, \"OptionsTest_X86-setOption\") {\n  // check if the callback and the instrumentation range are keep when use\n  // setOptions\n\n  InMemoryObject leaObj(\"leal 0x20(%eax), %eax\\nret\\n\");\n  QBDI::rword addr = (QBDI::rword)leaObj.getCode().data();\n\n  uint8_t *fakestack;\n  QBDI::GPRState *state = vm.getGPRState();\n  bool ret = QBDI::allocateVirtualStack(state, 4096, &fakestack);\n  REQUIRE(ret == true);\n  state->eax = 0;\n\n  vm.setOptions(QBDI::Options::NO_OPT);\n  vm.addInstrumentedRange(addr, addr + (QBDI::rword)leaObj.getCode().size());\n\n  bool cbReach = false;\n  vm.addCodeCB(QBDI::POSTINST, setBool, &cbReach);\n\n  QBDI::rword retval;\n\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->eax == 0x20);\n\n  cbReach = false;\n  vm.setOptions(QBDI::Options::OPT_ATT_SYNTAX);\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->eax == 0x40);\n\n  cbReach = false;\n  vm.setOptions(QBDI::Options::OPT_DISABLE_FPR);\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->eax == 0x60);\n\n  cbReach = false;\n  vm.setOptions(QBDI::Options::OPT_DISABLE_OPTIONAL_FPR);\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->eax == 0x80);\n\n  QBDI::alignedFree(fakestack);\n}\n"
  },
  {
    "path": "test/API/X86/VMTest_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"VMTest_X86.h\"\n\n#define MNEM_IMM_SHORT_VAL 66\n#define MNEM_IMM_VAL 42424242\n#define MNEM_IMM_SHORT_STRVAL \"66\"\n#define MNEM_IMM_STRVAL \"42424242\"\n\nconst struct TestInst TestInsts[MNEM_COUNT] = {\n    {3,\n     2,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 1, 8, 3, \"DH\",\n          QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, MNEM_IMM_SHORT_VAL, 1, 0,\n          -1, nullptr, QBDI::REGISTER_UNUSED},\n     }},\n    {3,\n     2,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 2, 0, 0, \"AX\",\n          QBDI::REGISTER_READ},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 2, 0, 1, \"BX\",\n          QBDI::REGISTER_READ},\n     }},\n    {5,\n     2,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, MNEM_IMM_VAL, 4, 0, -1,\n          nullptr, QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"EAX\",\n          QBDI::REGISTER_READ},\n     }},\n    {1,\n     5,\n     false,\n     QBDI::REGISTER_READ | QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          5, QBDI::GPR_NAMES[5], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          4, QBDI::GPR_NAMES[4], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_NONE, 0, 0, 0, -1, NULL,\n          QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"EDI\",\n          QBDI::REGISTER_READ_WRITE},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 4, \"ESI\",\n          QBDI::REGISTER_READ_WRITE},\n     }},\n    {4,\n     6,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, sizeof(QBDI::rword), 0,\n          0, QBDI::GPR_NAMES[0], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          4, QBDI::GPR_NAMES[4], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, sizeof(QBDI::rword), 0,\n          -1, NULL, QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          5, QBDI::GPR_NAMES[5], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 3, sizeof(QBDI::rword), 0,\n          -1, NULL, QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_NONE, 0, 0, 0, -1, NULL,\n          QBDI::REGISTER_UNUSED},\n     }},\n};\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0) {\n  QBDI::rword volatile res = arg0 + 0x666;\n  QBDI::rword p = 0x42;\n  QBDI::rword v[2] = {0x67, 0x45};\n#ifndef QBDI_PLATFORM_WINDOWS\n  asm(\"cmp $\" MNEM_IMM_SHORT_STRVAL \", %%dh\" ::: \"dh\");\n  asm(\"cmp %%bx, %%ax\" ::: \"bx\", \"ax\");\n  asm(\"cmp $\" MNEM_IMM_STRVAL \", %%eax\" ::: \"eax\"); // explicit register\n  asm(\"mov %0, %%edi; mov %1, %%esi; cmpsb %%es:(%%edi), (%%esi)\" ::\"r\"(&p),\n      \"r\"(&p)\n      : \"edi\", \"esi\");\n  asm(\"mov %0, %%edi; mov $1, %%esi; cmp 0x3(%%esi,%%edi,1), %%eax\" ::\"r\"(v)\n      : \"edi\", \"esi\", \"eax\");\n#endif\n  return res;\n}\n\n// clang-format off\nstd::vector<uint8_t> VMTest_X86_InvalidInstruction = {\n  0xb9, 0x64, 0x00, 0x00, 0x00,   // 00: mov    ecx,0x64\n  0x31, 0xc0,                     // 05: xor    eax,eax\n  0x31, 0xdb,                     // 07: xor    ebx,ebx\n  0x01, 0xc8,                     // 09: add    eax,ecx\n  0x83, 0xe9, 0x01,               // 0b: sub    ecx,0x1\n  0x83, 0xf9, 0x00,               // 0e: cmp    ecx,0x0\n  0x01, 0xc3,                     // 11: add    ebx,eax\n  0xff,                           // 13: invalid instruction\n};\n\nstd::vector<uint8_t> VMTest_X86_BreakingInstruction = {\n  0xb9, 0x64, 0x00, 0x00, 0x00,   // 00: mov    ecx,0x64\n  0x31, 0xc0,                     // 05: xor    eax,eax\n  0x31, 0xdb,                     // 07: xor    ebx,ebx\n  0x01, 0xc8,                     // 09: add    eax,ecx\n  0x83, 0xe9, 0x01,               // 0b: sub    ecx,0x1\n  0x83, 0xf9, 0x00,               // 0e: cmp    ecx,0x0\n  0x01, 0xc3,                     // 11: add    ebx,eax\n  0xc3,                           // 13: ret\n};\n\nstd::vector<uint8_t> VMTest_X86_SelfModifyingCode1 = {\n  0xe8, 0x00, 0x00, 0x00, 0x00,           // 00: call   $+5\n  0x58,                                   // 05: pop    eax\n  0xc6, 0x40, 0x14, 0xc3,                 // 06: mov    BYTE PTR [eax+0x14],0xc3\n  0xb9, 0x2a, 0x00, 0x00, 0x00,           // 0a: mov    ecx,0x2a\n  0x66, 0xc7, 0x40, 0x12, 0x01, 0xc8,     // 0f: mov    WORD PTR [eax+0x12],0xc801\n  0x31, 0xc0,                             // 15: xor    eax, eax\n  0x0f, 0xff,                             // 17: invalid instruction, replaced by 'add    eax,ecx'\n  0x55,                                   // 19: invalid instruction, replaced by 'ret'\n};\n\nstd::vector<uint8_t> VMTest_X86_SelfModifyingCode2 = {\n  0xe8, 0x00, 0x00, 0x00, 0x00,           // 00: call   $+5\n  0x58,                                   // 05: pop    eax\n  0xc6, 0x40, 0x12, 0xc3,                 // 06: mov    BYTE PTR [eax+0x12],0xc3\n  0xb9, 0x2a, 0x00, 0x00, 0x00,           // 0a: mov    ecx,0x2a\n  0x66, 0xc7, 0x40, 0x10, 0x89, 0xc8,     // 0f: mov    WORD PTR [eax+0x10],0xc889\n  0x31, 0xc0,                             // 15: xor    eax,eax   , 15: replaced by 'mov    eax,ecx'\n  0xff, 0xe0,                             // 17: jmp    eax       , 17: replaced by 'ret'\n};\n// clang-format on\n\nstd::unordered_map<std::string, SizedTestCode> TestCode = {\n    {\"VMTest-InvalidInstruction\", {VMTest_X86_InvalidInstruction, 0x11}},\n    {\"VMTest-BreakingInstruction\", {VMTest_X86_BreakingInstruction, 0x0b}},\n    {\"VMTest-SelfModifyingCode1\", {VMTest_X86_SelfModifyingCode1}},\n    {\"VMTest-SelfModifyingCode2\", {VMTest_X86_SelfModifyingCode2}}};\n"
  },
  {
    "path": "test/API/X86/VMTest_X86.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDITEST_VMTEST_X86_H\n#define QBDITEST_VMTEST_X86_H\n\n#include <unordered_map>\n#include <vector>\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/VM.h\"\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0);\n\n#define MNEM_COUNT 5u\n#define MNEM_VALIDATION 140u\n\n#define MAX_OPERAND 6\n#define MNEM_CMP \"CMP*\"\n\nstruct TestInst {\n  uint32_t instSize;\n  uint8_t numOperands;\n  bool isCompare;\n  QBDI::RegisterAccessType flagsAccess;\n  QBDI::OperandAnalysis operands[MAX_OPERAND];\n};\n\nstruct SizedTestCode {\n  std::vector<uint8_t> code;\n  std::size_t size;\n};\n\nextern const struct TestInst TestInsts[MNEM_COUNT];\nextern std::unordered_map<std::string, SizedTestCode> TestCode;\n\n#define SKIPTESTASM \"nop\\nnop\\nret\\n\"\n\n#endif\n"
  },
  {
    "path": "test/API/X86_64/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysisTest_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTest_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/OptionsTest_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/VMTest_X86_64.cpp\")\n"
  },
  {
    "path": "test/API/X86_64/InstAnalysisTest_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n\nstruct ExpectedInstAnalysis {\n  std::string mnemonic;\n  QBDI::rword address;\n  uint32_t instSize;\n  bool affectControlFlow;\n  bool isBranch;\n  bool isCall;\n  bool isReturn;\n  bool isCompare;\n  bool isPredicable;\n  bool mayLoad;\n  bool mayStore;\n  uint32_t loadSize;\n  uint32_t storeSize;\n  QBDI::ConditionType condition;\n};\n\nstatic void checkDisassembly(const QBDI::InstAnalysis *ana,\n                             const std::string &expect) {\n  CHECK(ana->disassembly == expect);\n}\n\n[[maybe_unused]] static void debugOperand(const QBDI::InstAnalysis *ana) {\n  if ((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n      QBDI::ANALYSIS_OPERANDS) {\n    for (int i = 0; i < ana->numOperands; i++) {\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      WARN(\"- type: \" << op.type << \", flag: \" << op.flag\n                      << \", value: \" << op.value << \", size: \" << (int)op.size\n                      << \", regOff : \" << (int)op.regOff\n                      << \", regCtxIdx: \" << op.regCtxIdx << \", regName: \"\n                      << (op.regName == nullptr ? \"nullptr\" : op.regName)\n                      << \", regAccess: \"\n                      << (op.regAccess & QBDI::REGISTER_READ ? \"r\" : \"-\")\n                      << (op.regAccess & QBDI::REGISTER_WRITE ? \"w\" : \"-\"));\n    }\n  }\n}\n\nstatic void checkOperand(const QBDI::InstAnalysis *ana,\n                         const std::vector<QBDI::OperandAnalysis> expecteds,\n                         QBDI::RegisterAccessType flagsAccess) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_OPERANDS) ==\n             QBDI::ANALYSIS_OPERANDS) {\n    CHECK(flagsAccess == ana->flagsAccess);\n    CHECK(expecteds.size() == ana->numOperands);\n    for (unsigned i = 0;\n         i < std::min<unsigned>(ana->numOperands, expecteds.size()); i++) {\n      const QBDI::OperandAnalysis &expect = expecteds[i];\n      const QBDI::OperandAnalysis &op = ana->operands[i];\n      INFO(\"For operand \" << i);\n\n      CHECK(expect.type == op.type);\n      CHECK(expect.flag == op.flag);\n      if (op.type == QBDI::OPERAND_IMM || expect.value != 0) {\n        CHECK(expect.value == op.value);\n      }\n      CHECK(expect.size == op.size);\n      CHECK(expect.regOff == op.regOff);\n      CHECK(expect.regCtxIdx == op.regCtxIdx);\n      CHECK(expect.regAccess == op.regAccess);\n\n      const std::string expectedRegName(\n          (expect.regName != nullptr) ? expect.regName : \"\");\n      const std::string foundRegName((op.regName != nullptr) ? op.regName : \"\");\n      CHECK(expectedRegName == foundRegName);\n\n      if (expect.regName == nullptr || op.regName == nullptr) {\n        CHECK(expect.regName == op.regName);\n      }\n    }\n  }\n}\n\nstatic void checkInst(const QBDI::InstAnalysis *ana,\n                      const ExpectedInstAnalysis expected) {\n\n  CHECKED_IF((ana->analysisType & QBDI::ANALYSIS_INSTRUCTION) ==\n             QBDI::ANALYSIS_INSTRUCTION) {\n    CHECK(expected.mnemonic == ana->mnemonic);\n    CHECK(expected.address == ana->address);\n    CHECK(expected.instSize == ana->instSize);\n    CHECK(expected.affectControlFlow == ana->affectControlFlow);\n    CHECK(expected.isBranch == ana->isBranch);\n    CHECK(expected.isCall == ana->isCall);\n    CHECK(expected.isReturn == ana->isReturn);\n    CHECK(expected.isCompare == ana->isCompare);\n    CHECK(expected.isPredicable == ana->isPredicable);\n    CHECK(expected.mayLoad == ana->mayLoad);\n    CHECK(expected.mayStore == ana->mayStore);\n    CHECK(expected.loadSize == ana->loadSize);\n    CHECK(expected.storeSize == ana->storeSize);\n    CHECK(expected.condition == ana->condition);\n  }\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-CachedInst\") {\n\n  QBDI::rword addr = genASM(\"leaq (%rax), %rbx\\n\");\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n\n  vm.clearAllCache();\n\n  CHECK(vm.getCachedInstAnalysis(addr) == nullptr);\n\n  vm.precacheBasicBlock(addr);\n\n  CHECK(vm.getCachedInstAnalysis(addr) != nullptr);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-lea\") {\n\n  QBDI::rword addr = genASM(\"leaq (%rax), %rbx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LEA64r\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tlea\\trbx, [rax]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1,\n                    \"RBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 0, \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 1, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_UNDEFINED_EFFECT,\n                    0, 0, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_UNDEFINED_EFFECT,\n                    0, 0, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-lea-same-reg\") {\n\n  QBDI::rword addr = genASM(\"leaq (%rax,%rax), %rax\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LEA64r\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tlea\\trax, [rax + rax]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 0, \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 1, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, 0, \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_UNDEFINED_EFFECT, 0, 8,\n                    0, -1, nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_UNDEFINED_EFFECT,\n                    0, 0, 0, -1, nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-movrm\") {\n\n  QBDI::rword addr = genASM(\"movq 0x45(%rax,%rdx,4), %rbx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV64rm\", addr,\n          /* instSize */ 5, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tmov\\trbx, qword ptr [rax + 4*rdx + 0x45]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1,\n                    \"RBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 3,\n                    \"RDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x45, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-movrm-seg\") {\n\n  QBDI::rword addr = genASM(\"movq %gs:0x45(%rax,%rdx,4), %rbx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV64rm\", addr,\n          /* instSize */ 6, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tmov\\trbx, qword ptr gs:[rax + 4*rdx + 0x45]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1,\n                    \"RBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 4, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 3,\n                    \"RDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x45, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_ADDR, 0, 2, 0, -1,\n                    \"GS\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-addmi\") {\n\n  QBDI::rword addr = genASM(\"addq\t$0x4157, (%rcx)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADD64mi32\", addr,\n          /* instSize */ 7, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 8, /* storeSize */ 8,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tadd\\tqword ptr [rcx], 0x4157\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, 0x4157, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-movrr\") {\n\n  QBDI::rword addr = genASM(\"mov %rcx, %rbx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV64rr\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tmov\\trbx, rcx\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1,\n                    \"RBX\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-movrr8\") {\n\n  QBDI::rword addr = genASM(\"mov %ch, %bl\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOV8rr\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tmov\\tbl, ch\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 1, 0, 1, \"BL\",\n                    QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 1, 8, 2, \"CH\",\n                    QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-xchgrr\") {\n\n  QBDI::rword addr = genASM(\"xchg %rcx, %rbx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"XCHG64rr\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\txchg\\trbx, rcx\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1,\n                    \"RBX\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-addrr\") {\n\n  QBDI::rword addr = genASM(\"add %rcx, %rbx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ADD64rr\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tadd\\trbx, rcx\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1,\n                    \"RBX\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-movsb\") {\n\n  QBDI::rword addr = genASM(\"movsb\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOVSB\", addr,\n          /* instSize */ 1, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 1, /* storeSize */ 1,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tmovsb\\tbyte ptr es:[rdi], byte ptr [rsi]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 5,\n                    \"RDI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 4,\n                    \"RSI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 5,\n                    \"EDI\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 4,\n                    \"ESI\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-cmpsb\") {\n\n  QBDI::rword addr = genASM(\"cmpsb\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMPSB\", addr,\n          /* instSize */ 1, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tcmpsb\\tbyte ptr [rsi], byte ptr es:[rdi]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 5,\n                    \"RDI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 4,\n                    \"RSI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 5,\n                    \"EDI\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 4,\n                    \"ESI\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_READ_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-cmpmr\") {\n\n  QBDI::rword addr = genASM(\"cmpq %rcx, (%rax,%rdx)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMP64mr\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tcmp\\tqword ptr [rax + rdx], rcx\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 3,\n                    \"RDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-cmprm\") {\n\n  QBDI::rword addr = genASM(\"cmpq (%rax,%rdx), %rcx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMP64rm\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ true,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tcmp\\trcx, qword ptr [rax + rdx]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 3,\n                    \"RDX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-ret\") {\n\n  QBDI::rword addr = genASM(\"retq\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"RET64\", addr,\n          /* instSize */ 1, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ true, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tret\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 15,\n                    \"RSP\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-call\") {\n\n  QBDI::rword addr = genASM(\"call test_custom_call\\ntest_custom_call:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CALL64pcrel32\", addr,\n          /* instSize */ 5, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 8,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tcall\\t0x0\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 0, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 15,\n                    \"RSP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, -1,\n                    \"SSP\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-callr\") {\n\n  QBDI::rword addr = genASM(\"callq *%rax\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CALL64r\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 8,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tcall\\trax\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 15,\n                    \"RSP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, -1,\n                    \"SSP\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-callm\") {\n\n  QBDI::rword addr = genASM(\"callq *0xa(%rax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CALL64m\", addr,\n          /* instSize */ 3, /* affectControlFlow */ true, /* isBranch */ false,\n          /* isCall */ true, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 8, /* storeSize */ 8,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tcall\\tqword ptr [rax + 0xa]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0xa, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 15,\n                    \"RSP\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_SEG, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, -1,\n                    \"SSP\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\n#define NOP8 \"nop\\nnop\\nnop\\nnop\\nnop\\nnop\\nnop\\nnop\\n\"\n#define NOP64 NOP8 NOP8 NOP8 NOP8 NOP8 NOP8 NOP8 NOP8\n#define NOP512 NOP64 NOP64 NOP64 NOP64 NOP64 NOP64 NOP64 NOP64\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-jmpi1\") {\n\n  QBDI::rword addr = genASM(\"jmp test_jmp\\n\" NOP8 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JMP_1\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tjmp\\t0x8\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 8, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-jmpi4\") {\n\n  QBDI::rword addr = genASM(\"jmp test_jmp\\n\" NOP512 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JMP_4\", addr,\n          /* instSize */ 5, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tjmp\\t0x200\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 512, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-je1\") {\n\n  QBDI::rword addr = genASM(\"je test_jmp\\n\" NOP8 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JCC_1\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tje\\t0x8\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 8, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-je4\") {\n\n  QBDI::rword addr = genASM(\"je test_jmp\\n\" NOP512 \"test_jmp:\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JCC_4\", addr,\n          /* instSize */ 6, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tje\\t0x200\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, 512, 4, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_READ);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-jmpm\") {\n\n  QBDI::rword addr = genASM(\"jmpq *0xa(%rax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"JMP64m\", addr,\n          /* instSize */ 3, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tjmp\\tqword ptr [rax + 0xa]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0xa, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-fldl\") {\n\n  QBDI::rword addr = genASM(\"fldl (%rax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LD_F64m\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 8, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tfld\\tqword ptr [rax]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x0, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 0,\n                    \"FPCW\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 2,\n                    \"FPSW\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-fstps\") {\n\n  QBDI::rword addr = genASM(\"fstps (%rax)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"ST_FP32m\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ true,\n          /* loadSize */ 0, /* storeSize */ 4,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tfstp\\tdword ptr [rax]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x0, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 0,\n                    \"FPCW\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 2, 0, 2,\n                    \"FPSW\", QBDI::REGISTER_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-movapd\") {\n\n  QBDI::rword addr = genASM(\"movapd (%rax), %xmm1\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOVAPDrm\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 16, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tmovapd\\txmm1, xmmword ptr [rax]\");\n  checkOperand(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n      {\n          {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0,\n           offsetof(QBDI::FPRState, xmm1), \"XMM1\", QBDI::REGISTER_WRITE},\n          {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0, \"RAX\",\n           QBDI::REGISTER_READ},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x0, 8, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n          {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1, nullptr,\n           QBDI::REGISTER_UNUSED},\n      },\n      QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-paddb\") {\n\n  QBDI::rword addr = genASM(\"paddb %mm1, %mm0\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MMX_PADDBrr\", addr,\n          /* instSize */ 3, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tpaddb\\tmm0, mm1\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 32,\n                    \"MM0\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 48,\n                    \"MM1\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-vpaddb\") {\n\n  QBDI::rword addr = genASM(\"vpaddb %xmm2, %xmm1, %xmm0\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"VPADDBrr\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tvpaddb\\txmm0, xmm1, xmm2\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 160,\n                    \"XMM0\", QBDI::REGISTER_WRITE},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 176,\n                    \"XMM1\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_FPR, QBDI::OPERANDFLAG_NONE, 0, 16, 0, 192,\n                    \"XMM2\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-xlatb\") {\n\n  QBDI::rword addr = genASM(\"xlatb\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"XLAT\", addr,\n          /* instSize */ 1, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\txlatb\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 1, 0, 0,\n                    \"AL\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_READ},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-movdir64b\") {\n\n  QBDI::rword addr = genASM(\"movdir64b 0xc(%rax), %rcx\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"MOVDIR64B64\", addr,\n          /* instSize */ 6, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 512, /* storeSize */ 512,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tmovdir64b\\trcx, zmmword ptr [rax + 0xc]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 0,\n                    \"RAX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0xc, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-loop\") {\n\n  QBDI::rword addr = genASM(\"target:\\n    loop target\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LOOP\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tloop\\t-0x2\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, -2, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-loope\") {\n\n  QBDI::rword addr = genASM(\"target:\\n    loope target\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LOOPE\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_EQUALS});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tloope\\t-0x2\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, -2, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-loopne\") {\n\n  QBDI::rword addr = genASM(\"target:\\n    loopne target\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"LOOPNE\", addr,\n          /* instSize */ 2, /* affectControlFlow */ true, /* isBranch */ true,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ false, /* mayStore */ false,\n          /* loadSize */ 0, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NOT_EQUALS});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tloopne\\t-0x2\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_PCREL, -2, 1, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 8, 0, 2,\n                    \"RCX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_UNUSED);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-repne-cmpsb\") {\n\n  QBDI::rword addr = genASM(\"repne cmpsb\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMPSB\", addr,\n          /* instSize */ 2, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ false,\n          /* loadSize */ 1, /* storeSize */ 0,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\trepne\\t\\tcmpsb\\tbyte ptr [rsi], byte ptr es:[rdi]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 5,\n                    \"RDI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 4,\n                    \"RSI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 5,\n                    \"EDI\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 4,\n                    \"ESI\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_READ_WRITE);\n}\n\nTEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64-lock_cmpxchg8b\") {\n\n  QBDI::rword addr = genASM(\"lock cmpxchg8b (%rsi)\\n\");\n\n  checkInst(\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_INSTRUCTION),\n      ExpectedInstAnalysis{\n          \"CMPXCHG8B\", addr,\n          /* instSize */ 4, /* affectControlFlow */ false, /* isBranch */ false,\n          /* isCall */ false, /* isReturn */ false, /* isCompare */ false,\n          /* isPredicable */ false, /* mayLoad */ true, /* mayStore */ true,\n          /* loadSize */ 8, /* storeSize */ 8,\n          /* condition */ QBDI::CONDITION_NONE});\n  checkDisassembly(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY),\n                   \"\\tlock\\t\\tcmpxchg8b\\tqword ptr [rsi]\");\n  checkOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS),\n               {\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, 8, 0, 4,\n                    \"RSI\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 0x0, 8, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_ADDR, 0, 0, 0, -1,\n                    nullptr, QBDI::REGISTER_UNUSED},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 0,\n                    \"EAX\", QBDI::REGISTER_READ_WRITE},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 1,\n                    \"EBX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 2,\n                    \"ECX\", QBDI::REGISTER_READ},\n                   {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_IMPLICIT, 0, 4, 0, 3,\n                    \"EDX\", QBDI::REGISTER_READ_WRITE},\n               },\n               QBDI::REGISTER_WRITE);\n}\n\n// TEST_CASE_METHOD(APITest, \"InstAnalysisTest_X86_64_tmp\") {\n//\n//   QBDI::rword addr = genASM(getenv(\"TMP_INST\"));\n//\n//   debugOperand(vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_OPERANDS));\n// }\n"
  },
  {
    "path": "test/API/X86_64/MemoryAccessTest_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/APITest.h\"\n\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/Range.h\"\n\n#include \"Utility/System.h\"\n\nstatic bool checkFeature(const char *f) {\n  if (!QBDI::isHostCPUFeaturePresent(f)) {\n    WARN(\"Host doesn't support \" << f << \" feature: SKIP\");\n    return false;\n  }\n  return true;\n}\n\n/*\nstatic QBDI::VMAction debugCB(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\nQBDI::FPRState *fprState, void *data) { const QBDI::InstAnalysis* instAnalysis =\nvm->getInstAnalysis(); printf(\"0x%lx (%10s): %s\\n\", instAnalysis->address,\ninstAnalysis->mnemonic, instAnalysis->disassembly);\n\n    for (auto &a: vm->getInstMemoryAccess()) {\n        printf(\" - inst: 0x%lx, addr: 0x%lx, size: %d, type: %c%c, value: 0x%lx,\nflags: 0x%x\\n\", a.instAddress, a.accessAddress, a.size,\n            ((a.type & QBDI::MEMORY_READ) !=0 )?'r':'-',\n            ((a.type & QBDI::MEMORY_WRITE) !=0 )?'w':'-',\n            a.value, a.flags);\n    }\n    return QBDI::VMAction::CONTINUE;\n}// */\n\nstruct ExpectedMemoryAccess {\n  QBDI::rword address;\n  QBDI::rword value;\n  uint16_t size;\n  QBDI::MemoryAccessType type;\n  QBDI::MemoryAccessFlags flags;\n  bool see = false;\n};\n\nstruct ExpectedMemoryAccesses {\n  std::vector<ExpectedMemoryAccess> accesses;\n};\n\nstatic QBDI::VMAction checkAccess(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n\n  ExpectedMemoryAccesses *info = static_cast<ExpectedMemoryAccesses *>(data);\n  std::vector<QBDI::MemoryAccess> memaccesses = vm->getInstMemoryAccess();\n  if (std::all_of(info->accesses.begin(), info->accesses.end(),\n                  [](ExpectedMemoryAccess &a) { return a.see; }))\n    return QBDI::VMAction::CONTINUE;\n\n  CHECKED_IF(memaccesses.size() == info->accesses.size()) {\n    for (size_t i = 0; i < info->accesses.size(); i++) {\n      auto &memaccess = memaccesses[i];\n      auto &expect = info->accesses[i];\n      CHECKED_IF(memaccess.accessAddress == expect.address)\n      CHECKED_IF((memaccess.value == expect.value || expect.value == 0))\n      CHECKED_IF(memaccess.size == expect.size)\n      CHECKED_IF(memaccess.type == expect.type)\n      CHECKED_IF(memaccess.flags == expect.flags)\n      expect.see = true;\n    }\n  }\n  return QBDI::VMAction::CONTINUE;\n}\n\n// test stack memory access\n// PUSH POP CALL RET\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-push_pop_reg\") {\n\n  const char source[] =\n      \"xchg %rsp, %rbx\\n\"\n      \"push %rax\\n\"\n      \"pop %rax\\n\"\n      \"xchg %rsp, %rbx\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61ae;\n  QBDI::rword tmpStack[10] = {0};\n  ExpectedMemoryAccesses expectedPush = {{\n      {(QBDI::rword)&tmpStack[8], v1, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPop = {{\n      {(QBDI::rword)&tmpStack[8], v1, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"PUSH64r\", QBDI::POSTINST, checkAccess, &expectedPush);\n  vm.addMnemonicCB(\"POP64r\", QBDI::PREINST, checkAccess, &expectedPop);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rbx = (QBDI::rword)&tmpStack[9];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPop.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPush.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-push_pop_mem\") {\n\n  const char source[] =\n      \"xchg %rsp, %rbx\\n\"\n      \"push (%rax)\\n\"\n      \"pop (%rax)\\n\"\n      \"xchg %rsp, %rbx\\n\";\n\n  QBDI::rword v1 = 0xab3672016bef61ae;\n  QBDI::rword tmpStack[10] = {0};\n  ExpectedMemoryAccesses expectedPushPre = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPushPost = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&tmpStack[8], v1, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPopPre = {{\n      {(QBDI::rword)&tmpStack[8], v1, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPopPost = {{\n      {(QBDI::rword)&tmpStack[8], v1, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"PUSH64rmm\", QBDI::PREINST, checkAccess, &expectedPushPre);\n  vm.addMnemonicCB(\"PUSH64rmm\", QBDI::POSTINST, checkAccess, &expectedPushPost);\n  vm.addMnemonicCB(\"POP64rmm\", QBDI::PREINST, checkAccess, &expectedPopPre);\n  vm.addMnemonicCB(\"POP64rmm\", QBDI::POSTINST, checkAccess, &expectedPopPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = (QBDI::rword)&v1;\n  state->rbx = (QBDI::rword)&tmpStack[9];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPopPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPopPost.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPushPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPushPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-call_ret\") {\n\n  const char source[] =\n      \"    xchg %rsp, %rbx\\n\"\n      \"    call test_call_ret_custom_call\\n\"\n      \"    jmp test_call_ret_custom_end\\n\"\n      \"test_call_ret_custom_call:\\n\"\n      \"    ret\\n\"\n      \"test_call_ret_custom_end:\\n\"\n      \"    xchg %rsp, %rbx\\n\";\n\n  QBDI::rword tmpStack[10] = {0};\n  ExpectedMemoryAccesses expectedCall = {{\n      {(QBDI::rword)&tmpStack[8], 0, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedRet = {{\n      {(QBDI::rword)&tmpStack[8], 0, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"CALL*\", QBDI::POSTINST, checkAccess, &expectedCall);\n  vm.addMnemonicCB(\"RET*\", QBDI::PREINST, checkAccess, &expectedRet);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rbx = (QBDI::rword)&tmpStack[9];\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedCall.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedRet.accesses)\n    CHECK(e.see);\n}\n\n// COMPS MOVS SCAS LODS STOS\n// REP and REPNE prefix\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-cmpsb\") {\n\n  const char source[] = \"cmpsb\\n\";\n\n  QBDI::rword v1 = 0xaa, v2 = 0x55;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v2, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-cmpsw\") {\n\n  const char source[] = \"cmpsw\\n\";\n\n  QBDI::rword v1 = 0x783, v2 = 0xbd7a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v2, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-cmpsd\") {\n\n  const char source[] = \"cmpsl\\n\";\n\n  QBDI::rword v1 = 0x6ef9efbd, v2 = 0xef783b2a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v2, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSL\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-cmpsq\") {\n\n  const char source[] = \"cmpsq\\n\";\n\n  QBDI::rword v1 = 0x6723870bdefa, v2 = 0x1234098765efdbac;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v2, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSQ\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-rep_cmpsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"rep cmpsb\\n\";\n\n  uint8_t v1[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6d};\n  uint8_t v2[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6c};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  state->rcx = sizeof(v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-repne_cmpsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"repne cmpsb\\n\";\n\n  uint8_t v1[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6d};\n  uint8_t v2[10] = {0xb1, 0x5, 0x98, 0xae, 0xe2, 0xe6, 0x19, 0xf9, 0xc7, 0x6d};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  state->rcx = sizeof(v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-rep_cmpsb2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"rep cmpsb\\n\"\n      \"cld\\n\";\n\n  uint8_t v1[10] = {0x5c, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6c};\n  uint8_t v2[10] = {0x56, 0x78, 0x89, 0xab, 0xe6, 0xe7, 0x1a, 0xfa, 0xc8, 0x6c};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[9], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2[9], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1[9];\n  state->rdi = (QBDI::rword)&v2[9];\n  state->rcx = sizeof(v1);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-rep_cmpsw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"rep cmpsw\\n\";\n\n  uint16_t v1[5] = {0x5c78, 0x89ab, 0xe6e7, 0x1afa, 0xc86c};\n  uint16_t v2[5] = {0x5c78, 0x89ab, 0xe6e7, 0x1afa, 0xc86d};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  state->rcx = sizeof(v1) / sizeof(uint16_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-rep_cmpsw2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"rep cmpsw\\n\"\n      \"cld\\n\";\n\n  uint16_t v1[5] = {0x5c78, 0x89ab, 0xe6e7, 0x1afa, 0xc86c};\n  uint16_t v2[5] = {0x5678, 0x89ab, 0xe6e7, 0x1afa, 0xc86c};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[4], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n      {(QBDI::rword)&v2[4], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, 10, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"CMPSW\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1[4];\n  state->rdi = (QBDI::rword)&v2[4];\n  state->rcx = sizeof(v1) / sizeof(uint16_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"movsb\\n\";\n\n  QBDI::rword v1 = 0xbf, v2 = 0x78;\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSB\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVSB\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"movsw\\n\";\n\n  QBDI::rword v1 = 0x789f, v2 = 0xbd67;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"movsl\\n\";\n\n  QBDI::rword v1 = 0xa579eb9d, v2 = 0x2389befa;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsq\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"movsq\\n\";\n\n  QBDI::rword v1 = 0xb036789eb8ea, v2 = 0xab8e602baef846;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 8, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSQ\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsb2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"movsb\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x7f;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsw2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"movsw\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0xad63, v2 = 0x6219;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsl2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"movsl\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0xefa036db, v2 = 0xefd7137a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movsq2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"movsq\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x2360Abed083, v2 = 0xeb0367a801346;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&v2, v1, 8, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSQ\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v2 == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-rep_movsl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"rep movsl\\n\";\n\n  uint32_t v1[5] = {0xab673, 0xeba9256, 0x638feba8, 0x7182faB, 0x7839021b};\n  uint32_t v2[5] = {0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1, 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, sizeof(v1), QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, sizeof(v1), QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  state->rdi = (QBDI::rword)&v2;\n  state->rcx = sizeof(v1) / sizeof(uint32_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (size_t i = 0; i < sizeof(v1) / sizeof(uint32_t); i++)\n    CHECK(v2[i] == v1[i]);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-rep_movsl2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"rep movsl\\n\"\n      \"cld\\n\";\n\n  uint32_t v1[5] = {0xab673, 0xeba9256, 0x638feba8, 0x7182faB, 0x7839021b};\n  uint32_t v2[5] = {0};\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v1[4], 0, 0, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE | QBDI::MEMORY_UNKNOWN_SIZE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v1, 0, sizeof(v1), QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v2, 0, sizeof(v1), QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVSL\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1[4];\n  state->rdi = (QBDI::rword)&v2[4];\n  state->rcx = sizeof(v1) / sizeof(uint32_t);\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (size_t i = 0; i < sizeof(v1) / sizeof(uint32_t); i++)\n    CHECK(v2[i] == v1[i]);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-scasb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"scasb\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v2, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SCASB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-scasw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"scasw\\n\";\n\n  QBDI::rword v1 = 0x5ef1, v2 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v2, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SCASW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-scasl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"scasl\\n\";\n\n  QBDI::rword v1 = 0x629ebf, v2 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v2, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SCASL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-scasq\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"scasq\\n\";\n\n  QBDI::rword v1 = 0x6efab792eb, v2 = 0xebaf719630145;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v2, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"SCASQ\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-lodsb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"lodsb\\n\";\n\n  QBDI::rword v1 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LODSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(retval == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-lodsw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"lodsw\\n\";\n\n  QBDI::rword v1 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 2, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LODSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(retval == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-lodsl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"lodsl\\n\";\n\n  QBDI::rword v1 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LODSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(retval == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-lodsq\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"lodsq\\n\";\n\n  QBDI::rword v1 = 0xebaf719630145;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v1, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LODSQ\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rsi = (QBDI::rword)&v1;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(retval == v1);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosb\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"stosb\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosw\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"stosw\\n\";\n\n  QBDI::rword v1 = 0x5ef1, v2 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosl\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"stosl\\n\";\n\n  QBDI::rword v1 = 0x629ebf, v2 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosq\") {\n\n  const char source[] =\n      \"cld\\n\"\n      \"stosq\\n\";\n\n  QBDI::rword v1 = 0x6efab792eb, v2 = 0xebaf719630145;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 8, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSQ\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosb2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"stosb\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x8, v2 = 0x6a;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 1, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSB\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosw2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"stosw\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x5ef1, v2 = 0x6789;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 2, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSW\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosl2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"stosl\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x629ebf, v2 = 0x1234567;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSL\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-stosq2\") {\n\n  const char source[] =\n      \"std\\n\"\n      \"stosq\\n\"\n      \"cld\\n\";\n\n  QBDI::rword v1 = 0x6efab792eb, v2 = 0xebaf719630145;\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v2, v1, 8, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"STOSQ\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v1;\n  state->rdi = (QBDI::rword)&v2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v1 == v2);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movzx\") {\n\n  const char source[] = \"movzbq  0x5(%rbx), %rax\\n\";\n\n  uint8_t v[] = {0xeb, 0xaf, 0x71, 0x96, 0x30, 0x14, 0x52, 0xce};\n\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v[5], v[5], 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVZX64rm8\", QBDI::POSTINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = 0x6efab792eb;\n  state->rbx = (QBDI::rword)&v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(state->rax == v[5]);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-addmr\") {\n\n  const char source[] = \"addq %rax, (%rbx)\\n\";\n\n  const uint64_t v1 = 0xebaf7196761ef1;\n  const uint64_t v2 = 0xfab792ebaec56913;\n  uint64_t buff = v1;\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&buff, v1 + v2, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"ADD64mr\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"ADD64mr\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = v2;\n  state->rbx = (QBDI::rword)&buff;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(buff == v1 + v2);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-xchgrm\") {\n\n  const char source[] = \"xchgq %rax, (%rax)\\n\";\n\n  const uint64_t v1 = 0xebaf7196761ef1;\n  uint64_t buff = v1;\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff, v1, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&buff, (QBDI::rword)&buff, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"XCHG64rm\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"XCHG64rm\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = (QBDI::rword)&buff;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(state->rax == v1);\n  CHECK(buff == (uint64_t)&buff);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-enter-leave\") {\n\n  const char source[] =\n      \"xchg %rsp, %rbx\\n\"\n      \"enter $0x0, $0x0\\n\"\n      \"leave\\n\"\n      \"xchg %rsp, %rbx\\n\";\n\n  const uint64_t v = 0x79819abe76;\n  QBDI::rword tmpStack[10] = {0};\n\n  ExpectedMemoryAccesses expectedEnter = {{\n      {(QBDI::rword)&tmpStack[8], v, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedLeave = {{\n      {(QBDI::rword)&tmpStack[8], v, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"ENTER\", QBDI::POSTINST, checkAccess, &expectedEnter);\n  vm.addMnemonicCB(\"LEAVE*\", QBDI::PREINST, checkAccess, &expectedLeave);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rbx = (QBDI::rword)&tmpStack[9];\n  state->rbp = v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(state->rbp == v);\n  CHECK(state->rbx == (QBDI::rword)&tmpStack[9]);\n  for (auto &e : expectedEnter.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedLeave.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-fld-fstp\") {\n\n  if (!checkFeature(\"mmx\")) {\n    return;\n  }\n\n  const char source[] =\n      \"flds\t(%rax)\\n\"\n      \"fldl (%rbx)\\n\"\n      \"movl\t$0x0, (%rax)\\n\"\n      \"movl\t$0x0, (%rbx)\\n\"\n      \"fstpl (%rbx)\\n\"\n      \"fstps (%rax)\\n\";\n\n  const uint32_t v1 = 0x416ac41e;\n  const uint64_t v2 = 0x79819abe76;\n  uint32_t buff1 = v1;\n  uint64_t buff2 = v2;\n\n  ExpectedMemoryAccesses expectedLoad32 = {{\n      {(QBDI::rword)&buff1, v1, 4, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedLoad64 = {{\n      {(QBDI::rword)&buff2, v2, 8, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedStore64 = {{\n      {(QBDI::rword)&buff2, v2, 8, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedStore32 = {{\n      {(QBDI::rword)&buff1, v1, 4, QBDI::MEMORY_WRITE, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"LD_F32m\", QBDI::PREINST, checkAccess, &expectedLoad32);\n  vm.addMnemonicCB(\"LD_F64m\", QBDI::PREINST, checkAccess, &expectedLoad64);\n  vm.addMnemonicCB(\"ST_FP64m\", QBDI::POSTINST, checkAccess, &expectedStore64);\n  vm.addMnemonicCB(\"ST_FP32m\", QBDI::POSTINST, checkAccess, &expectedStore32);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = (QBDI::rword)&buff1;\n  state->rbx = (QBDI::rword)&buff2;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(buff1 == v1);\n  CHECK(buff2 == v2);\n  for (auto &e : expectedLoad32.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedLoad64.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedStore64.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedStore32.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movapd\") {\n\n  if (!checkFeature(\"sse2\")) {\n    return;\n  }\n\n  const char source[] =\n      \"movapd\t(%rax), %xmm1\\n\"\n      \"movapd %xmm2, (%rbx)\\n\";\n\n  const uint8_t v1[16] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27,\n                          0x67, 0x4f, 0x91, 0x6e, 0x4b, 0x57, 0x4d, 0xc9};\n  const uint8_t v2[16] = {0xa9, 0x5d, 0x27, 0x6a, 0xc4, 0x91, 0x6e, 0x4b,\n                          0x57, 0x4d, 0x41, 0x6a, 0x0e, 0x80, 0xeb, 0xad};\n  QBDI_ALIGNED(16) uint8_t buff1[16];\n  QBDI_ALIGNED(16) uint8_t buff2[16] = {0};\n\n  memcpy(buff1, v1, sizeof(v1));\n\n  ExpectedMemoryAccesses expectedLoad = {{\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedStore = {{\n      {(QBDI::rword)&buff2, 0, 16, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVAPDrm\", QBDI::PREINST, checkAccess, &expectedLoad);\n  vm.addMnemonicCB(\"MOVAPDmr\", QBDI::POSTINST, checkAccess, &expectedStore);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = (QBDI::rword)&buff1;\n  state->rbx = (QBDI::rword)&buff2;\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  memset(fstate->xmm1, '\\x00', sizeof(v1));\n  memcpy(fstate->xmm2, v2, sizeof(v2));\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(memcmp(fstate->xmm2, buff2, sizeof(v2)) == 0);\n  CHECK(memcmp(fstate->xmm1, v1, sizeof(v1)) == 0);\n  for (auto &e : expectedLoad.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedStore.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-maskmovdqu\") {\n\n  if (!checkFeature(\"avx\")) {\n    return;\n  }\n\n  const char source[] = \"maskmovdqu\t%xmm1, %xmm0\\n\";\n\n  const uint8_t v1[16] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27,\n                          0x67, 0x4f, 0x91, 0x6e, 0x4b, 0x57, 0x4d, 0xc9};\n  const uint8_t v2[16] = {0xa9, 0x5d, 0x27, 0x6a, 0xc4, 0x91, 0x6e, 0x4b,\n                          0x57, 0x4d, 0x41, 0x6a, 0x0e, 0x80, 0xeb, 0xad};\n  const uint8_t mask[16] = {0x80, 0x80, 0x80, 0x80, 0x0, 0x80, 0x0,  0x80,\n                            0x80, 0x0,  0x80, 0x0,  0x0, 0x80, 0x80, 0x0};\n  QBDI_ALIGNED(16) uint8_t buff1[16];\n\n  memcpy(buff1, v1, sizeof(v1));\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_READ,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&buff1, 0, 16, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MASKMOVDQU64\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MASKMOVDQU64\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rdi = (QBDI::rword)&buff1;\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  memcpy(fstate->xmm0, v2, sizeof(v2));\n  memcpy(fstate->xmm1, mask, sizeof(mask));\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (unsigned i = 0; i < sizeof(buff1); i++)\n    CHECK(((mask[i] == 0) ? v1[i] : v2[i]) == buff1[i]);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-maskmovq\") {\n\n  if (!checkFeature(\"avx\") || !checkFeature(\"sse\")) {\n    return;\n  }\n\n  const char source[] = \"maskmovq\t%mm1, %mm0\\n\";\n\n  const uint8_t v1[8] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27};\n  const uint8_t v2[8] = {0xa9, 0x5d, 0x27, 0x6a, 0xc4, 0x91, 0x6e, 0x4b};\n  const uint8_t mask[8] = {0x80, 0x80, 0x80, 0x80, 0x0, 0x80, 0x0, 0x80};\n  QBDI_ALIGNED(8) uint8_t buff1[8];\n\n  memcpy(buff1, v1, sizeof(v1));\n\n  const uint64_t readValue = 0x275da9141ec46a41;\n  const uint64_t writeValue = 0x4b5d91146a275da9;\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&buff1, readValue, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&buff1, readValue, 8, QBDI::MEMORY_READ,\n       QBDI::MEMORY_NO_FLAGS},\n      {(QBDI::rword)&buff1, writeValue, 8, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MMX_MASKMOVQ64\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MMX_MASKMOVQ64\", QBDI::POSTINST, checkAccess,\n                   &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rdi = (QBDI::rword)&buff1;\n  vm.setGPRState(state);\n\n  QBDI::FPRState *fstate = vm.getFPRState();\n  memcpy(fstate->stmm0.reg, v2, sizeof(v2));\n  memcpy(fstate->stmm1.reg, mask, sizeof(mask));\n  vm.setFPRState(fstate);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (unsigned i = 0; i < sizeof(buff1); i++)\n    CHECK(((mask[i] == 0) ? v1[i] : v2[i]) == buff1[i]);\n\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-xlat\") {\n\n  const char source[] = \"xlatb\\n\";\n\n  const uint8_t v[8] = {0x41, 0x6a, 0xc4, 0x1e, 0x14, 0xa9, 0x5d, 0x27};\n\n  ExpectedMemoryAccesses expected = {{\n      {(QBDI::rword)&v[5], v[5], 1, QBDI::MEMORY_READ, QBDI::MEMORY_NO_FLAGS},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"XLAT\", QBDI::PREINST, checkAccess, &expected);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rbx = (QBDI::rword)&v;\n  state->rax = 5;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  CHECK(v[5] == vm.getGPRState()->rax);\n  for (auto &e : expected.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-movdir64b\") {\n\n  if (!checkFeature(\"movdir64b\")) {\n    return;\n  }\n\n  const char source[] = \"movdir64b (%rax), %rcx\\n\";\n\n  uint8_t v[512] = {0};\n  uint8_t buff[512] = {0};\n\n  for (size_t i = 0; i < sizeof(v); i++) {\n    v[i] = (i % 256);\n  }\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, 0, 512, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v, 0, 512, QBDI::MEMORY_READ, QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&buff, 0, 512, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"MOVDIR64B64\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"MOVDIR64B64\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = (QBDI::rword)&v;\n  state->rcx = (QBDI::rword)&buff;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (size_t i = 0; i < sizeof(v); i++)\n    CHECK(buff[i] == (i % 256));\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n\nTEST_CASE_METHOD(APITest, \"MemoryAccessTest_X86_64-xsave\") {\n\n  if (!checkFeature(\"xsave\")) {\n    return;\n  }\n\n  const char source[] = \"xsave (%rcx)\\n\";\n\n  QBDI_ALIGNED(64) uint8_t v[4096] = {0};\n\n  ExpectedMemoryAccesses expectedPre = {{\n      {(QBDI::rword)&v, 0, 576, QBDI::MEMORY_READ,\n       QBDI::MEMORY_MINIMUM_SIZE | QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n  ExpectedMemoryAccesses expectedPost = {{\n      {(QBDI::rword)&v, 0, 576, QBDI::MEMORY_READ,\n       QBDI::MEMORY_MINIMUM_SIZE | QBDI::MEMORY_UNKNOWN_VALUE},\n      {(QBDI::rword)&v, 0, 576, QBDI::MEMORY_WRITE,\n       QBDI::MEMORY_MINIMUM_SIZE | QBDI::MEMORY_UNKNOWN_VALUE},\n  }};\n\n  vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm.addMnemonicCB(\"XSAVE*\", QBDI::PREINST, checkAccess, &expectedPre);\n  vm.addMnemonicCB(\"XSAVE*\", QBDI::POSTINST, checkAccess, &expectedPost);\n\n  QBDI::GPRState *state = vm.getGPRState();\n  state->rax = 7;\n  state->rdx = 0;\n  state->rcx = (QBDI::rword)&v;\n  vm.setGPRState(state);\n\n  QBDI::rword retval;\n  bool ran = runOnASM(&retval, source);\n\n  CHECK(ran);\n  for (auto &e : expectedPre.accesses)\n    CHECK(e.see);\n  for (auto &e : expectedPost.accesses)\n    CHECK(e.see);\n}\n"
  },
  {
    "path": "test/API/X86_64/OptionsTest_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"API/OptionsTest.h\"\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include \"inttypes.h\"\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n\nTEST_CASE_METHOD(OptionsTest, \"OptionsTest_X86_64-ATTSyntax\") {\n\n  InMemoryObject leaObj(\"leaq (%rax), %rbx\\nret\\n\");\n  QBDI::rword addr = (QBDI::rword)leaObj.getCode().data();\n\n  vm.setOptions(QBDI::Options::NO_OPT);\n  vm.precacheBasicBlock(addr);\n\n  const QBDI::InstAnalysis *ana =\n      vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY);\n  REQUIRE(ana != nullptr);\n  REQUIRE(ana->disassembly != nullptr);\n\n  CHECK(std::string(ana->disassembly) == std::string(\"\\tlea\\trbx, [rax]\"));\n\n  vm.clearAllCache();\n  vm.setOptions(QBDI::Options::OPT_ATT_SYNTAX);\n  vm.precacheBasicBlock(addr);\n\n  ana = vm.getCachedInstAnalysis(addr, QBDI::ANALYSIS_DISASSEMBLY);\n  REQUIRE(ana != nullptr);\n  REQUIRE(ana->disassembly != nullptr);\n\n  CHECK(std::string(ana->disassembly) == std::string(\"\\tleaq\\t(%rax), %rbx\"));\n}\n\nstatic QBDI::VMAction setBool(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                              QBDI::FPRState *fprState, void *data) {\n  *((bool *)data) = true;\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(OptionsTest, \"OptionsTest_X86_64-setOption\") {\n  // check if the callback and the instrumentation range are keep when use\n  // setOptions\n\n  InMemoryObject leaObj(\"leaq 0x20(%rax), %rax\\nret\\n\");\n  QBDI::rword addr = (QBDI::rword)leaObj.getCode().data();\n\n  uint8_t *fakestack;\n  QBDI::GPRState *state = vm.getGPRState();\n  bool ret = QBDI::allocateVirtualStack(state, 4096, &fakestack);\n  REQUIRE(ret == true);\n  state->rax = 0;\n\n  vm.setOptions(QBDI::Options::NO_OPT);\n  vm.addInstrumentedRange(addr, addr + (QBDI::rword)leaObj.getCode().size());\n\n  bool cbReach = false;\n  vm.addCodeCB(QBDI::POSTINST, setBool, &cbReach);\n\n  QBDI::rword retval;\n\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->rax == 0x20);\n\n  cbReach = false;\n  vm.setOptions(QBDI::Options::OPT_ATT_SYNTAX);\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->rax == 0x40);\n\n  cbReach = false;\n  vm.setOptions(QBDI::Options::OPT_DISABLE_FPR);\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->rax == 0x60);\n\n  cbReach = false;\n  vm.setOptions(QBDI::Options::OPT_DISABLE_OPTIONAL_FPR);\n  REQUIRE(vm.call(&retval, addr, {}));\n  REQUIRE(cbReach);\n  REQUIRE(state->rax == 0x80);\n\n  QBDI::alignedFree(fakestack);\n}\n"
  },
  {
    "path": "test/API/X86_64/VMTest_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"VMTest_X86_64.h\"\n\n#define MNEM_IMM_SHORT_VAL 66\n#define MNEM_IMM_VAL 42424242\n#define MNEM_IMM_SHORT_STRVAL \"66\"\n#define MNEM_IMM_STRVAL \"42424242\"\n\nconst struct TestInst TestInsts[MNEM_COUNT] = {\n    {3,\n     2,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 1, 8, 3, \"DH\",\n          QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, MNEM_IMM_SHORT_VAL, 1, 0,\n          -1, nullptr, QBDI::REGISTER_UNUSED},\n     }},\n    {3,\n     2,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 0, \"RAX\",\n          QBDI::REGISTER_READ},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 8, 0, 1, \"RBX\",\n          QBDI::REGISTER_READ},\n     }},\n    {5,\n     2,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_NONE, MNEM_IMM_VAL, 4, 0, -1,\n          nullptr, QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 0, \"EAX\",\n          QBDI::REGISTER_READ},\n     }},\n    {1,\n     5,\n     false,\n     QBDI::REGISTER_READ | QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          5, QBDI::GPR_NAMES[5], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          4, QBDI::GPR_NAMES[4], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_NONE, 0, 0, 0, -1, NULL,\n          QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 5, \"EDI\",\n          QBDI::REGISTER_READ_WRITE},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, 4, 0, 4, \"ESI\",\n          QBDI::REGISTER_READ_WRITE},\n     }},\n    {5,\n     6,\n     true,\n     QBDI::REGISTER_WRITE,\n     {\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_NONE, 0, sizeof(QBDI::rword), 0,\n          0, QBDI::GPR_NAMES[0], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          4, QBDI::GPR_NAMES[4], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 1, sizeof(QBDI::rword), 0,\n          -1, NULL, QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_GPR, QBDI::OPERANDFLAG_ADDR, 0, sizeof(QBDI::rword), 0,\n          5, QBDI::GPR_NAMES[5], QBDI::REGISTER_READ},\n         {QBDI::OPERAND_IMM, QBDI::OPERANDFLAG_ADDR, 3, sizeof(QBDI::rword), 0,\n          -1, NULL, QBDI::REGISTER_UNUSED},\n         {QBDI::OPERAND_INVALID, QBDI::OPERANDFLAG_NONE, 0, 0, 0, -1, NULL,\n          QBDI::REGISTER_UNUSED},\n     }},\n};\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0) {\n  QBDI::rword volatile res = arg0 + 0x666;\n  QBDI::rword p = 0x42;\n  QBDI::rword v[2] = {0x67, 0x45};\n#ifndef QBDI_PLATFORM_WINDOWS\n  asm(\"cmp $\" MNEM_IMM_SHORT_STRVAL \", %%dh\" ::: \"dh\");\n  asm(\"cmp %%rbx, %%rax\" ::: \"rbx\", \"rax\");\n  asm(\"cmp $\" MNEM_IMM_STRVAL \", %%eax\" ::: \"eax\"); // explicit register\n  asm(\"movq %0, %%rdi; movq %1, %%rsi; cmpsb %%es:(%%rdi), (%%rsi)\" ::\"r\"(&p),\n      \"r\"(&p)\n      : \"rdi\", \"rsi\");\n  asm(\"mov %0, %%rdi; mov $1, %%rsi; cmp 0x3(%%rsi,%%rdi,1), %%rax\" ::\"r\"(v)\n      : \"rdi\", \"rsi\", \"rax\");\n#endif\n  return res;\n}\n\n// clang-format off\nstd::vector<uint8_t> VMTest_X86_64_InvalidInstruction = {\n  0x48, 0xc7, 0xc1, 0x64, 0x00, 0x00, 0x00,   // 00: mov    rcx,0x64\n  0x48, 0x31, 0xc0,                           // 07: xor    rax,rax\n  0x48, 0x01, 0xc8,                           // 0a: add    rax,rcx\n  0x48, 0x83, 0xe9, 0x01,                     // 0d: sub    rcx,0x1\n  0x48, 0x83, 0xf9, 0x00,                     // 11: cmp    rcx,0x0\n  0xfe, 0x34,                                 // 15: invalid instruction\n};\n\nstd::vector<uint8_t> VMTest_X86_64_BreakingInstruction = {\n  0x48, 0xc7, 0xc1, 0x64, 0x00, 0x00, 0x00,   // 00: mov    rcx,0x64\n  0x48, 0x31, 0xc0,                           // 07: xor    rax,rax\n  0x48, 0x01, 0xc8,                           // 0a: add    rax,rcx\n  0x48, 0x83, 0xe9, 0x01,                     // 0d: sub    rcx,0x1\n  0x48, 0x83, 0xf9, 0x00,                     // 11: cmp    rcx,0x0\n  0xc3,                                       // 15: ret\n};\n\nstd::vector<uint8_t> VMTest_X86_64_SelfModifyingCode1 = {\n  0xe8, 0x00, 0x00, 0x00, 0x00,               // 00: call   $+5\n  0x58,                                       // 05: pop    rax\n  0xc6, 0x40, 0x13, 0xc3,                     // 06: mov    BYTE PTR [rax+0x13],0xc3\n  0xc7, 0x40, 0x0f, 0x48, 0x83, 0xc0, 0x2a,   // 0a: mov    DWORD PTR [rax+0xf],0x2ac08348\n  0x48, 0x31, 0xc0,                           // 11: xor    rax,rax\n  0x06, 0xa2, 0x06, 0xcc,                     // 14: invalid instruction, replaced by 'add    rax,0x2a'\n  0x06,                                       // 18: invalid instruction, replaced by 'ret'\n};\n\nstd::vector<uint8_t> VMTest_X86_64_SelfModifyingCode2 = {\n  0xe8, 0x00, 0x00, 0x00, 0x00,               // 00: call   $+5\n  0x58,                                       // 05: pop    rax\n  0xc6, 0x40, 0x13, 0xc3,                     // 06: mov    BYTE PTR [rax+0x13],0xc3\n  0xc7, 0x40, 0x0f, 0x48, 0x83, 0xc0, 0x2a,   // 0a: mov    DWORD PTR [rax+0xf],0x2ac08348\n  0x48, 0x31, 0xc0,                           // 11: xor    rax,rax\n  0xff, 0xe0,                                 // 14: jmp    rax       , 14: replaced by 'add    rax,0x2a'\n  0x48, 0x31, 0xc9,                           // 16: xor    rcx,rcx   , 18: replaced by 'ret'\n  0xcc,                                       // 19: int3\n};\n// clang-format on\n\nstd::unordered_map<std::string, SizedTestCode> TestCode = {\n    {\"VMTest-InvalidInstruction\", {VMTest_X86_64_InvalidInstruction, 0x11}},\n    {\"VMTest-BreakingInstruction\", {VMTest_X86_64_BreakingInstruction, 0x0d}},\n    {\"VMTest-SelfModifyingCode1\", {VMTest_X86_64_SelfModifyingCode1}},\n    {\"VMTest-SelfModifyingCode2\", {VMTest_X86_64_SelfModifyingCode2}}};\n"
  },
  {
    "path": "test/API/X86_64/VMTest_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDITEST_VMTEST_X86_64_H\n#define QBDITEST_VMTEST_X86_64_H\n\n#include <unordered_map>\n#include <vector>\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"QBDI/VM.h\"\n\nQBDI_NOINLINE QBDI::rword satanicFun(QBDI::rword arg0);\n\n#define MNEM_COUNT 5u\n#define MNEM_VALIDATION 140u\n\n#define MAX_OPERAND 6\n#define MNEM_CMP \"CMP*\"\n\nstruct TestInst {\n  uint32_t instSize;\n  uint8_t numOperands;\n  bool isCompare;\n  QBDI::RegisterAccessType flagsAccess;\n  QBDI::OperandAnalysis operands[MAX_OPERAND];\n};\n\nstruct SizedTestCode {\n  std::vector<uint8_t> code;\n  std::size_t size;\n};\n\nextern const struct TestInst TestInsts[MNEM_COUNT];\nextern std::unordered_map<std::string, SizedTestCode> TestCode;\n\n#define SKIPTESTASM \"nop\\nnop\\nret\\n\"\n\n#endif\n"
  },
  {
    "path": "test/Benchmark/CMakeLists.txt",
    "content": "include(FetchContent)\n\nFetchContent_Populate(\n  sha256_lib\n  URL \"https://github.com/aguinet/sha256_literal/archive/d7017a7b4bbc30bc93fb8bd4cf54555986d25ef0.zip\"\n  URL_HASH\n    \"SHA256=7f7c3771764829ec74ee3f9cfc9df6250051f92d6e7f2e1e11f06a0b48938b2c\"\n  DOWNLOAD_DIR \"${QBDI_THIRD_PARTY_DIRECTORY}/sha256_lib-download\"\n  SOURCE_DIR \"${FETCHCONTENT_BASE_DIR}/sha256_lib-src\"\n  BINARY_DIR \"${FETCHCONTENT_BASE_DIR}/sha256_lib-build\"\n  SUBBUILD_DIR \"${FETCHCONTENT_BASE_DIR}/sha256_lib-subbuild\"\n  QUIET)\n\nadd_custom_command(\n  OUTPUT \"${sha256_lib_SOURCE_DIR}/sha256_impl.cpp\"\n  COMMAND\n    ${CMAKE_COMMAND} -E create_symlink \"${sha256_lib_SOURCE_DIR}/sha256.cpp\"\n    \"${sha256_lib_SOURCE_DIR}/sha256_impl.cpp\")\n\ntarget_include_directories(QBDIBenchmark PRIVATE \"${sha256_lib_SOURCE_DIR}\")\n\n# set sources\ntarget_sources(\n  QBDIBenchmark\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/Fibonacci.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/SHA256.cpp\"\n          \"${sha256_lib_SOURCE_DIR}/sha256_impl.cpp\")\n"
  },
  {
    "path": "test/Benchmark/Fibonacci.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <QBDI.h>\n\n#define CATCH_CONFIG_ENABLE_BENCHMARKING\n#include <catch2/benchmark/catch_benchmark.hpp>\n#include <catch2/catch_test_macros.hpp>\n\nQBDI_NOINLINE QBDI::rword Fibonacci(QBDI::rword number) {\n  if (number < 2)\n    return 1;\n  return Fibonacci(number - 1) + Fibonacci(number - 2);\n}\n\nstatic QBDI::VMAction eventCB(QBDI::VMInstanceRef vm,\n                              const QBDI::VMState *vmState,\n                              QBDI::GPRState *gprState,\n                              QBDI::FPRState *fprState, void *data) {\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction eventMemoryCB(QBDI::VMInstanceRef vm,\n                                    const QBDI::VMState *vmState,\n                                    QBDI::GPRState *gprState,\n                                    QBDI::FPRState *fprState, void *data) {\n  unsigned *v = static_cast<unsigned *>(data);\n  *v += vm->getBBMemoryAccess().size();\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction instEmptyCB(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction instCB(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                             QBDI::FPRState *fprState, void *data) {\n  unsigned *v = static_cast<unsigned *>(data);\n  const QBDI::InstAnalysis *instAnalysis =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION |\n                          QBDI::ANALYSIS_DISASSEMBLY | QBDI::ANALYSIS_OPERANDS);\n  *v += instAnalysis->instSize;\n\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction instMemoryCB(QBDI::VMInstanceRef vm,\n                                   QBDI::GPRState *gprState,\n                                   QBDI::FPRState *fprState, void *data) {\n  unsigned *v = static_cast<unsigned *>(data);\n  *v += vm->getInstMemoryAccess().size();\n\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE(\"Benchmark_Fibonacci\") {\n\n  BENCHMARK(\"Fibonacci(2)\") { return Fibonacci(2); };\n\n  BENCHMARK_ADVANCED(\"Fibonacci(2) with QBDI uncached\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(2)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK(\"Fibonacci(20)\") { return Fibonacci(20); };\n\n  BENCHMARK_ADVANCED(\"Fibonacci(20) with QBDI\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    meter.measure([&] {\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\"Fibonacci(20) with QBDI uncached\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\"Fibonacci(20) with QBDI uncached with VMEvent\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    // add vm event\n    vm.addVMEventCB(QBDI::SEQUENCE_EXIT, eventCB, nullptr);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\"Fibonacci(20) with QBDI uncached with InstCallback\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    // add callback\n    vm.addCodeCB(QBDI::PREINST, instEmptyCB, nullptr);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"Fibonacci(20) with QBDI uncached with InstCallback and InstAnalysis\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    // add callback\n    unsigned v = 0;\n    vm.addCodeCB(QBDI::PREINST, instCB, &v);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\"Fibonacci(20) with QBDI uncached with MemoryAccess\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"Fibonacci(20) with QBDI uncached with MemoryAccess and \"\n      \"getBBMemoryAccess\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n\n    // add callback\n    unsigned v = 0;\n    vm.addVMEventCB(QBDI::SEQUENCE_EXIT, eventMemoryCB, &v);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"Fibonacci(20) with QBDI uncached with MemoryAccess and MemoryCallback\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(reinterpret_cast<QBDI::rword>(Fibonacci));\n\n    vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n\n    // add callback\n    unsigned v = 0;\n    vm.addMemAccessCB(QBDI::MEMORY_READ_WRITE, instMemoryCB, &v);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(Fibonacci),\n              {static_cast<QBDI::rword>(20)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n}\n"
  },
  {
    "path": "test/Benchmark/SHA256.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdint.h>\n\n#include \"sha256.h\"\n#include \"QBDI.h\"\n\n#define CATCH_CONFIG_ENABLE_BENCHMARKING\n#include <catch2/benchmark/catch_benchmark.hpp>\n#include <catch2/catch_test_macros.hpp>\n\nstatic const uint8_t buffer[1 << 12] = {0};\n\nQBDI_NOINLINE sha256::HashType *compute_sha(size_t l) {\n  sha256::HashType *hash = new sha256::HashType();\n  *hash = sha256::compute(buffer, std::min(l, sizeof(buffer)));\n  return hash;\n}\n\nstatic QBDI::VMAction eventCB(QBDI::VMInstanceRef vm,\n                              const QBDI::VMState *vmState,\n                              QBDI::GPRState *gprState,\n                              QBDI::FPRState *fprState, void *data) {\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction eventMemoryCB(QBDI::VMInstanceRef vm,\n                                    const QBDI::VMState *vmState,\n                                    QBDI::GPRState *gprState,\n                                    QBDI::FPRState *fprState, void *data) {\n  unsigned *v = static_cast<unsigned *>(data);\n  *v += vm->getBBMemoryAccess().size();\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction instEmptyCB(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction instCB(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                             QBDI::FPRState *fprState, void *data) {\n  unsigned *v = static_cast<unsigned *>(data);\n  const QBDI::InstAnalysis *instAnalysis =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION |\n                          QBDI::ANALYSIS_DISASSEMBLY | QBDI::ANALYSIS_OPERANDS);\n  *v += instAnalysis->instSize;\n\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction instMemoryCB(QBDI::VMInstanceRef vm,\n                                   QBDI::GPRState *gprState,\n                                   QBDI::FPRState *fprState, void *data) {\n  unsigned *v = static_cast<unsigned *>(data);\n  *v += vm->getInstMemoryAccess().size();\n\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE(\"Benchmark_sha256\") {\n\n  BENCHMARK(\"sha256(len: 16 Bytes)\") { return compute_sha(16); };\n\n  BENCHMARK_ADVANCED(\"sha256(len: 16 Bytes) with QBDI uncached\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha), {16});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK(\"sha256(len: 4KBytes)\") { return compute_sha(sizeof(buffer)); };\n\n  BENCHMARK_ADVANCED(\"sha256(len: 4KBytes) with QBDI\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    meter.measure([&] {\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\"sha256(len: 4KBytes) with QBDI uncached\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\"sha256(len: 4KBytes) with QBDI uncached with VMEvent\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    // add vm event\n    vm.addVMEventCB(QBDI::SEQUENCE_EXIT, eventCB, nullptr);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"sha256(len: 4KBytes) with QBDI uncached with InstCallback\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    // add callback\n    vm.addCodeCB(QBDI::PREINST, instEmptyCB, nullptr);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"sha256(len: 4KBytes) with QBDI uncached with InstCallback and \"\n      \"InstAnalysis\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    // add callback\n    unsigned v = 0;\n    vm.addCodeCB(QBDI::PREINST, instCB, &v);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"sha256(len: 4KBytes) with QBDI uncached with MemoryAccess\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"sha256(len: 4KBytes) with QBDI uncached with MemoryAccess and \"\n      \"getBBMemoryAccess\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n\n    // add callback\n    unsigned v = 0;\n    vm.addVMEventCB(QBDI::SEQUENCE_EXIT, eventMemoryCB, &v);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n\n  BENCHMARK_ADVANCED(\n      \"sha256(len: 4KBytes) with QBDI uncached with MemoryAccess and \"\n      \"MemoryCallback\")\n  (Catch::Benchmark::Chronometer meter) {\n    // init QBDI\n    QBDI::VM vm;\n    uint8_t *fakestack = nullptr;\n\n    // alloc stack\n    QBDI::allocateVirtualStack(vm.getGPRState(), 1 << 20, &fakestack);\n\n    // instrument QBDI\n    vm.addInstrumentedModuleFromAddr(\n        reinterpret_cast<QBDI::rword>(compute_sha));\n\n    vm.recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n\n    // add callback\n    unsigned v = 0;\n    vm.addMemAccessCB(QBDI::MEMORY_READ_WRITE, instMemoryCB, &v);\n\n    meter.measure([&] {\n      vm.clearAllCache();\n      QBDI::rword ret_value = 0;\n      vm.call(&ret_value, reinterpret_cast<QBDI::rword>(compute_sha),\n              {sizeof(buffer)});\n      return ret_value;\n    });\n    QBDI::alignedFree(fakestack);\n  };\n}\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "# test\nif(QBDI_TEST)\n\n  add_executable(QBDITest \"${CMAKE_CURRENT_LIST_DIR}/QBDITest.cpp\")\n  add_signature(QBDITest)\n\n  include(\"${CMAKE_CURRENT_LIST_DIR}/API/CMakeLists.txt\")\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ExecBlock/CMakeLists.txt\")\n  include(\"${CMAKE_CURRENT_LIST_DIR}/Patch/CMakeLists.txt\")\n  include(\"${CMAKE_CURRENT_LIST_DIR}/Miscs/CMakeLists.txt\")\n  include(\"${CMAKE_CURRENT_LIST_DIR}/TestSetup/CMakeLists.txt\")\n\n  target_include_directories(\n    QBDITest\n    PRIVATE \"${CMAKE_CURRENT_BINARY_DIR}/../include\"\n            \"${CMAKE_CURRENT_SOURCE_DIR}/../include\"\n            \"${CMAKE_CURRENT_SOURCE_DIR}\" \"${CMAKE_CURRENT_SOURCE_DIR}/../src\")\n\n  target_link_libraries(QBDITest QBDI_static qbdi-llvm Catch2::Catch2 spdlog)\n  target_compile_options(QBDITest\n                         PUBLIC $<$<COMPILE_LANGUAGE:C>:${QBDI_COMMON_C_FLAGS}>)\n  target_compile_options(\n    QBDITest PUBLIC $<$<COMPILE_LANGUAGE:CXX>:${QBDI_COMMON_CXX_FLAGS}>)\n  target_compile_definitions(QBDITest PUBLIC ${QBDI_COMMON_DEFINITION})\n\n  set_target_properties(QBDITest PROPERTIES CXX_STANDARD 17\n                                            CXX_STANDARD_REQUIRED ON)\n\n  add_test(NAME QBDITest COMMAND QBDITest)\nendif()\n\n# Benchmark\nif(QBDI_BENCHMARK)\n  add_executable(QBDIBenchmark \"${CMAKE_CURRENT_LIST_DIR}/QBDIBenchmark.cpp\")\n  add_signature(QBDIBenchmark)\n\n  include(\"${CMAKE_CURRENT_LIST_DIR}/Benchmark/CMakeLists.txt\")\n\n  target_include_directories(\n    QBDIBenchmark\n    PRIVATE \"${CMAKE_CURRENT_BINARY_DIR}/../include\"\n            \"${CMAKE_CURRENT_SOURCE_DIR}/../include\"\n            \"${CMAKE_CURRENT_SOURCE_DIR}\")\n\n  target_compile_options(QBDIBenchmark\n                         PUBLIC $<$<COMPILE_LANGUAGE:C>:${QBDI_COMMON_C_FLAGS}>)\n  target_compile_options(\n    QBDIBenchmark PUBLIC $<$<COMPILE_LANGUAGE:CXX>:${QBDI_COMMON_CXX_FLAGS}>)\n  target_link_libraries(QBDIBenchmark QBDI_static Catch2::Catch2)\n\n  set_target_properties(QBDIBenchmark PROPERTIES CXX_STANDARD 17\n                                                 CXX_STANDARD_REQUIRED ON)\nendif()\n"
  },
  {
    "path": "test/ExecBlock/AARCH64/CMakeLists.txt",
    "content": "target_sources(QBDITest\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/PatchEmptyAARCH64.cpp\")\n"
  },
  {
    "path": "test/ExecBlock/AARCH64/PatchEmptyAARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ExecBlock/PatchEmpty.h\"\n\n#include \"AArch64InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n\nQBDI::Patch generateEmptyPatch(QBDI::rword address,\n                               const QBDI::LLVMCPUs &llvmcpus) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::AArch64::HINT);\n  inst.addOperand(llvm::MCOperand::createImm(0));\n\n  const QBDI::LLVMCPU &llvmcpu = llvmcpus.getCPU(QBDI::CPUMode::DEFAULT);\n\n  QBDI::Patch p{inst, address, 4, llvmcpu};\n  p.finalizeInstsPatch();\n  return p;\n}\n"
  },
  {
    "path": "test/ExecBlock/ARM/CMakeLists.txt",
    "content": "target_sources(QBDITest PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/PatchEmptyARM.cpp\")\n"
  },
  {
    "path": "test/ExecBlock/ARM/PatchEmptyARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ExecBlock/PatchEmpty.h\"\n\n#include \"Patch/ARM/Layer2_ARM.h\"\n#include \"llvm/MC/MCInst.h\"\n\nQBDI::Patch generateEmptyPatch(QBDI::rword address,\n                               const QBDI::LLVMCPUs &llvmcpus) {\n  llvm::MCInst inst = QBDI::nop();\n\n  const QBDI::LLVMCPU &llvmcpu = llvmcpus.getCPU(QBDI::CPUMode::DEFAULT);\n\n  QBDI::Patch p{inst, address, 4, llvmcpu};\n  p.finalizeInstsPatch();\n  return p;\n}\n"
  },
  {
    "path": "test/ExecBlock/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockTest.cpp\"\n                   \"${CMAKE_CURRENT_LIST_DIR}/ExecBlockManagerTest.cpp\")\n\nif(QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "test/ExecBlock/ExecBlockManagerTest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <memory>\n\n#include \"ExecBlockManagerTest.h\"\n#include \"PatchEmpty.h\"\n\n#include \"QBDI/State.h\"\n#include \"ExecBlock/Context.h\"\n#include \"ExecBlock/ExecBlock.h\"\n#include \"ExecBlock/ExecBlockManager.h\"\n#include \"Patch/ExecBlockPatch.h\"\n#include \"Patch/InstMetadata.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/RelocatableInst.h\"\n\nQBDI::Patch::Vec getEmptyBB(QBDI::rword address,\n                            const QBDI::LLVMCPUs &llvmcpu) {\n  QBDI::Patch::Vec bb;\n  bb.push_back(generateEmptyPatch(address, llvmcpu));\n  return bb;\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest,\n                 \"ExecBlockManagerTest-BasicBlockLookup\") {\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n\n  execBlockManager.writeBasicBlock(getEmptyBB(0x42424240, *this), 1);\n  REQUIRE(nullptr == execBlockManager.getProgrammedExecBlock(\n                         0x13371338, QBDI::CPUMode::DEFAULT));\n  REQUIRE(nullptr != execBlockManager.getProgrammedExecBlock(\n                         0x42424240, QBDI::CPUMode::DEFAULT));\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest, \"ExecBlockManagerTest-ClearCache\") {\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n\n  execBlockManager.writeBasicBlock(getEmptyBB(0x42424240, *this), 1);\n  REQUIRE(nullptr != execBlockManager.getProgrammedExecBlock(\n                         0x42424240, QBDI::CPUMode::DEFAULT));\n  execBlockManager.clearCache();\n  REQUIRE(nullptr == execBlockManager.getProgrammedExecBlock(\n                         0x42424240, QBDI::CPUMode::DEFAULT));\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest, \"ExecBlockManagerTest-ExecBlockReuse\") {\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n\n  execBlockManager.writeBasicBlock(getEmptyBB(0x42424240, *this), 1);\n  execBlockManager.writeBasicBlock(getEmptyBB(0x42424244, *this), 1);\n  REQUIRE(execBlockManager.getProgrammedExecBlock(0x42424240,\n                                                  QBDI::CPUMode::DEFAULT) ==\n          execBlockManager.getProgrammedExecBlock(0x42424244,\n                                                  QBDI::CPUMode::DEFAULT));\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest,\n                 \"ExecBlockManagerTest-ExecBlockRegions\") {\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n\n  execBlockManager.writeBasicBlock(getEmptyBB(0x42424240, *this), 1);\n  execBlockManager.writeBasicBlock(getEmptyBB(0x24242424, *this), 1);\n  REQUIRE(execBlockManager.getProgrammedExecBlock(0x42424240,\n                                                  QBDI::CPUMode::DEFAULT) !=\n          execBlockManager.getProgrammedExecBlock(0x24242424,\n                                                  QBDI::CPUMode::DEFAULT));\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest, \"ExecBlockManagerTest-ExecBlockAlloc\") {\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n  QBDI::rword address = 0;\n\n  for (address = 0; address < 0x1000; address++) {\n    execBlockManager.writeBasicBlock(getEmptyBB(address, *this), 1);\n  }\n\n  REQUIRE(\n      execBlockManager.getProgrammedExecBlock(0, QBDI::CPUMode::DEFAULT) !=\n      execBlockManager.getProgrammedExecBlock(0xfff, QBDI::CPUMode::DEFAULT));\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest, \"ExecBlockManagerTest-CacheRewrite\") {\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n  unsigned int i = 0;\n\n  execBlockManager.writeBasicBlock(getEmptyBB(0x42424240, *this), 1);\n  QBDI::ExecBlock *block1 = execBlockManager.getProgrammedExecBlock(\n      0x42424240, QBDI::CPUMode::DEFAULT);\n  for (i = 0; i < 0x1000; i++) {\n    execBlockManager.writeBasicBlock(getEmptyBB(0x42424240, *this), 1);\n  }\n  QBDI::ExecBlock *block2 = execBlockManager.getProgrammedExecBlock(\n      0x42424240, QBDI::CPUMode::DEFAULT);\n\n  REQUIRE(block1 == block2);\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest,\n                 \"ExecBlockManagerTest-MultipleBasicBlockExecution\") {\n  const QBDI::LLVMCPU &llvmcpu = this->getCPU(QBDI::CPUMode::DEFAULT);\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n  QBDI::ExecBlock *block = nullptr;\n  // Jit two different terminators\n  QBDI::Patch::Vec terminator1 = getEmptyBB(0x42424240, *this);\n  QBDI::Patch::Vec terminator2 = getEmptyBB(0x13371338, *this);\n  terminator1[0].append(QBDI::getTerminator(llvmcpu, 0x42424240));\n  terminator1[0].metadata.modifyPC = true;\n  terminator2[0].append(QBDI::getTerminator(llvmcpu, 0x13371338));\n  terminator2[0].metadata.modifyPC = true;\n  execBlockManager.writeBasicBlock(std::move(terminator1), 1);\n  execBlockManager.writeBasicBlock(std::move(terminator2), 1);\n  // Execute the two basic block and get the value of PC from the data block\n  block = execBlockManager.getProgrammedExecBlock(0x42424240,\n                                                  QBDI::CPUMode::DEFAULT);\n  REQUIRE(nullptr != block);\n  block->execute();\n  REQUIRE((QBDI::rword)0x42424240 ==\n          QBDI_GPR_GET(&block->getContext()->gprState, QBDI::REG_PC));\n\n  block = execBlockManager.getProgrammedExecBlock(0x13371338,\n                                                  QBDI::CPUMode::DEFAULT);\n  REQUIRE(nullptr != block);\n  block->execute();\n  REQUIRE((QBDI::rword)0x13371338 ==\n          QBDI_GPR_GET(&block->getContext()->gprState, QBDI::REG_PC));\n}\n\nTEST_CASE_METHOD(ExecBlockManagerTest, \"ExecBlockManagerTest-Stresstest\") {\n  const unsigned align = 4;\n  const QBDI::LLVMCPU &llvmcpu = this->getCPU(QBDI::CPUMode::DEFAULT);\n  QBDI::ExecBlockManager execBlockManager(*this, &this->vm);\n  QBDI::rword address = 0;\n\n  for (address = 0; address < 1000 * align; address += align) {\n    QBDI::Patch::Vec terminator = getEmptyBB(address, *this);\n    terminator[0].append(QBDI::getTerminator(llvmcpu, address));\n    terminator[0].metadata.modifyPC = true;\n    execBlockManager.writeBasicBlock(std::move(terminator), 1);\n    QBDI::ExecBlock *block = execBlockManager.getProgrammedExecBlock(\n        address, QBDI::CPUMode::DEFAULT);\n    REQUIRE(nullptr != block);\n    block->execute();\n    REQUIRE(address ==\n            QBDI_GPR_GET(&block->getContext()->gprState, QBDI::REG_PC));\n  }\n  for (; address > 0; address -= align) {\n    QBDI::ExecBlock *block = execBlockManager.getProgrammedExecBlock(\n        address - align, QBDI::CPUMode::DEFAULT);\n    REQUIRE(nullptr != block);\n    block->execute();\n    REQUIRE(address - align ==\n            QBDI_GPR_GET(&block->getContext()->gprState, QBDI::REG_PC));\n  }\n}\n"
  },
  {
    "path": "test/ExecBlock/ExecBlockManagerTest.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDITEST_EXECBLOCKMANAGERTEST_H\n#define QBDITEST_EXECBLOCKMANAGERTEST_H\n\n#include \"TestSetup/LLVMTestEnv.h\"\n#include \"QBDI/VM.h\"\n\nclass ExecBlockManagerTest : public LLVMTestEnv {\npublic:\n  QBDI::VM vm;\n};\n\n#endif /* QBDITEST_EXECBLOCKMANAGERTEST_H */\n"
  },
  {
    "path": "test/ExecBlock/ExecBlockTest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <stdio.h>\n\n#include \"ExecBlockTest.h\"\n#include \"PatchEmpty.h\"\n\n#include \"ExecBlock/ExecBlock.h\"\n#include \"Patch/ExecBlockPatch.h\"\n#include \"Patch/Patch.h\"\n#include \"Patch/PatchRule.h\"\n#include \"Patch/RelocatableInst.h\"\n\nTEST_CASE_METHOD(ExecBlockTest, \"ExecBlockTest-EmptyBasicBlock\") {\n  // Allocate ExecBlock\n  QBDI::ExecBlock execBlock(*this, &this->vm);\n  // Write an empty basic block\n  QBDI::Patch::Vec empty;\n  QBDI::SeqWriteResult res =\n      execBlock.writeSequence(empty.begin(), empty.end());\n  REQUIRE(res.seqID == QBDI::EXEC_BLOCK_FULL);\n}\n\nTEST_CASE_METHOD(ExecBlockTest, \"ExecBlockTest-MultipleBasicBlock\") {\n  const QBDI::LLVMCPU &llvmcpu = this->getCPU(QBDI::CPUMode::DEFAULT);\n  // Allocate ExecBlock\n  QBDI::ExecBlock execBlock(*this, &this->vm);\n  // Jit two different terminators\n  QBDI::Patch::Vec terminator1;\n  QBDI::Patch::Vec terminator2;\n  terminator1.push_back(generateEmptyPatch(0x42424240, *this));\n  terminator2.push_back(generateEmptyPatch(0x13371338, *this));\n  terminator1[0].append(QBDI::getTerminator(llvmcpu, 0x42424240));\n  terminator1[0].metadata.modifyPC = true;\n  terminator2[0].append(QBDI::getTerminator(llvmcpu, 0x13371338));\n  terminator2[0].metadata.modifyPC = true;\n  QBDI::SeqWriteResult block1 =\n      execBlock.writeSequence(terminator1.begin(), terminator1.end());\n  QBDI::SeqWriteResult block2 =\n      execBlock.writeSequence(terminator2.begin(), terminator2.end());\n  // Are the seqID valid?\n  REQUIRE(block2.seqID > block1.seqID);\n  // Execute the two basic block and get the value of PC from the data block\n  execBlock.selectSeq(block1.seqID);\n  execBlock.execute();\n  QBDI::rword pc1 =\n      QBDI_GPR_GET(&execBlock.getContext()->gprState, QBDI::REG_PC);\n  execBlock.selectSeq(block2.seqID);\n  execBlock.execute();\n  QBDI::rword pc2 =\n      QBDI_GPR_GET(&execBlock.getContext()->gprState, QBDI::REG_PC);\n  execBlock.selectSeq(block1.seqID);\n  execBlock.execute();\n  QBDI::rword pc3 =\n      QBDI_GPR_GET(&execBlock.getContext()->gprState, QBDI::REG_PC);\n  // block1 and block2 should have different PC values\n  REQUIRE(pc1 != pc2);\n  REQUIRE(pc1 == pc3);\n}\n\nTEST_CASE_METHOD(ExecBlockTest, \"ExecBlockTest-BasicBlockOverload\") {\n  // Allocate ExecBlock\n  QBDI::ExecBlock execBlock(*this, &this->vm);\n  QBDI::Patch::Vec empty;\n  QBDI::SeqWriteResult res;\n  uint32_t i = 0;\n\n  while ((res = execBlock.writeSequence(empty.begin(), empty.end())).seqID !=\n         0xFFFF) {\n    REQUIRE(res.seqID == i);\n    REQUIRE(execBlock.getEpilogueOffset() >= (uint32_t)0);\n    execBlock.selectSeq(res.seqID);\n    execBlock.execute();\n    i++;\n  }\n  INFO(\"Maximum basic block per exec block: \" << i);\n}\n"
  },
  {
    "path": "test/ExecBlock/ExecBlockTest.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef QBDITEST_EXECBLOCKTEST_H\n#define QBDITEST_EXECBLOCKTEST_H\n\n#include \"TestSetup/LLVMTestEnv.h\"\n#include \"QBDI/VM.h\"\n\nclass ExecBlockTest : public LLVMTestEnv {\npublic:\n  QBDI::VM vm;\n};\n\n#endif /* QBDITEST_EXECBLOCKTEST_H */\n"
  },
  {
    "path": "test/ExecBlock/PatchEmpty.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef QBDITEST_PATCHEMPTY_H\n#define QBDITEST_PATCHEMPTY_H\n\n#include \"QBDI/State.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Patch/Patch.h\"\n\nQBDI::Patch generateEmptyPatch(QBDI::rword address,\n                               const QBDI::LLVMCPUs &llvmcpu);\n\n#endif /* QBDITEST_EXECBLOCKTEST_H */\n"
  },
  {
    "path": "test/ExecBlock/X86/CMakeLists.txt",
    "content": "target_sources(QBDITest PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/PatchEmptyX86.cpp\")\n"
  },
  {
    "path": "test/ExecBlock/X86/PatchEmptyX86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ExecBlock/PatchEmpty.h\"\n\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n\nQBDI::Patch generateEmptyPatch(QBDI::rword address,\n                               const QBDI::LLVMCPUs &llvmcpus) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::NOOP);\n\n  const QBDI::LLVMCPU &llvmcpu = llvmcpus.getCPU(QBDI::CPUMode::DEFAULT);\n\n  QBDI::Patch p{inst, address, 1, llvmcpu};\n  p.finalizeInstsPatch();\n  return p;\n}\n"
  },
  {
    "path": "test/ExecBlock/X86_64/CMakeLists.txt",
    "content": "target_sources(QBDITest\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/PatchEmptyX86_64.cpp\")\n"
  },
  {
    "path": "test/ExecBlock/X86_64/PatchEmptyX86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ExecBlock/PatchEmpty.h\"\n\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n\nQBDI::Patch generateEmptyPatch(QBDI::rword address,\n                               const QBDI::LLVMCPUs &llvmcpus) {\n  llvm::MCInst inst;\n\n  inst.setOpcode(llvm::X86::NOOP);\n\n  const QBDI::LLVMCPU &llvmcpu = llvmcpus.getCPU(QBDI::CPUMode::DEFAULT);\n\n  QBDI::Patch p{inst, address, 1, llvmcpu};\n  p.finalizeInstsPatch();\n  return p;\n}\n"
  },
  {
    "path": "test/Miscs/CMakeLists.txt",
    "content": "target_sources(QBDITest PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/StringTest.cpp\")\n"
  },
  {
    "path": "test/Miscs/StringTest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n\n#include \"Utility/String.h\"\n\nTEST_CASE(\"startsWithTest-SimpleMatches\") {\n  CHECK(QBDI::String::startsWith(\"JMP\", \"JMP64\"));\n  CHECK_FALSE(QBDI::String::startsWith(\"xMP\", \"JMP\"));\n  CHECK_FALSE(QBDI::String::startsWith(\"JMP64\", \"x\"));\n}\n\nTEST_CASE(\"startsWithTest-NullPointers\") {\n  CHECK_FALSE(QBDI::String::startsWith(\"JMP64\", NULL));\n  CHECK_FALSE(QBDI::String::startsWith(NULL, \"x\"));\n  CHECK_FALSE(QBDI::String::startsWith(NULL, NULL));\n}\n\nTEST_CASE(\"startsWithTest-WildCardBasic\") {\n  CHECK(QBDI::String::startsWith(\"J*\", \"JMP\"));\n  CHECK_FALSE(QBDI::String::startsWith(\"J*\", \"xMP\"));\n}\n\nTEST_CASE(\"startsWithTest-WildCardAdvanced\") {\n  CHECK(QBDI::String::startsWith(\"J*P\", \"JMP\"));\n  CHECK_FALSE(QBDI::String::startsWith(\"J*P\", \"JMx\"));\n  CHECK(QBDI::String::startsWith(\"JMP*\", \"JMP\"));\n  CHECK(QBDI::String::startsWith(\"*\", \"\"));\n  CHECK(QBDI::String::startsWith(\"*\", \"JMP\"));\n}\n\nTEST_CASE(\"startsWithTest-Prefix\") {\n  CHECK(QBDI::String::startsWith(\"B\", \"B64\"));\n  CHECK(QBDI::String::startsWith(\"B*\", \"B64\"));\n  CHECK_FALSE(QBDI::String::startsWith(\"B\", \"BIQ\"));\n  CHECK(QBDI::String::startsWith(\"B*\", \"BIQ\"));\n}\n"
  },
  {
    "path": "test/Patch/AARCH64/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/ComparedExecutor_AARCH64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/LLVMOperandInfo_AARCH64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTable_AARCH64.cpp\")\n"
  },
  {
    "path": "test/Patch/AARCH64/ComparedExecutor_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"ComparedExecutor_AARCH64.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"Patch/Utils.h\"\n\nconst char CPU[] = CPU_CPU;\nconst std::vector<std::string> MATTRS = {CPU_MATTRS};\n\nInMemoryObject\nComparedExecutor_AARCH64::compileWithContextSwitch(const char *source) {\n  std::ostringstream finalSource;\n\n  finalSource << \"stp x29, lr, [sp, #-16]!\\n\"\n              << \"add x7, x1, #\" << offsetof(QBDI::Context, fprState.v0)\n              << \"\\n\";\n  for (uint32_t i = 0; i < QBDI_NUM_FPR; i = i + 4) {\n    finalSource << \"ld1 {v\" << i << \".2d-v\" << i + 3 << \".2d}, [x7], #\"\n                << sizeof(__uint128_t) * 4 << \"\\n\";\n  }\n  finalSource\n      << \"ldr x0, [x1, #\" << offsetof(QBDI::Context, gprState.nzcv) << \"]\\n\"\n      << \"msr nzcv, x0\\n\"\n      << \"ldr x0, [x1, #\" << offsetof(QBDI::Context, fprState.fpcr) << \"]\\n\"\n      << \"msr fpcr, x0\\n\"\n      << \"ldr x0, [x1, #\" << offsetof(QBDI::Context, fprState.fpsr) << \"]\\n\"\n      << \"msr fpsr, x0\\n\"\n      << \"ldr x2, [x1, #\" << offsetof(QBDI::Context, gprState.x2) << \"]\\n\"\n      << \"ldr x3, [x1, #\" << offsetof(QBDI::Context, gprState.x3) << \"]\\n\"\n      << \"ldr x4, [x1, #\" << offsetof(QBDI::Context, gprState.x4) << \"]\\n\"\n      << \"ldr x5, [x1, #\" << offsetof(QBDI::Context, gprState.x5) << \"]\\n\"\n      << \"ldr x6, [x1, #\" << offsetof(QBDI::Context, gprState.x6) << \"]\\n\"\n      << \"ldr x7, [x1, #\" << offsetof(QBDI::Context, gprState.x7) << \"]\\n\"\n      << \"ldr x8, [x1, #\" << offsetof(QBDI::Context, gprState.x8) << \"]\\n\"\n      << \"ldr x9, [x1, #\" << offsetof(QBDI::Context, gprState.x9) << \"]\\n\"\n      << \"ldr x10, [x1, #\" << offsetof(QBDI::Context, gprState.x10) << \"]\\n\"\n      << \"ldr x11, [x1, #\" << offsetof(QBDI::Context, gprState.x11) << \"]\\n\"\n      << \"ldr x12, [x1, #\" << offsetof(QBDI::Context, gprState.x12) << \"]\\n\"\n      << \"ldr x13, [x1, #\" << offsetof(QBDI::Context, gprState.x13) << \"]\\n\"\n      << \"ldr x14, [x1, #\" << offsetof(QBDI::Context, gprState.x14) << \"]\\n\"\n      << \"ldr x15, [x1, #\" << offsetof(QBDI::Context, gprState.x15) << \"]\\n\"\n      << \"ldr x16, [x1, #\" << offsetof(QBDI::Context, gprState.x16) << \"]\\n\"\n      << \"ldr x17, [x1, #\" << offsetof(QBDI::Context, gprState.x17) << \"]\\n\"\n#if not(defined(QBDI_PLATFORM_MACOS) || defined(QBDI_PLATFORM_IOS))\n      << \"ldr x18, [x1, #\" << offsetof(QBDI::Context, gprState.x18) << \"]\\n\"\n#endif\n      << \"ldr x19, [x1, #\" << offsetof(QBDI::Context, gprState.x19) << \"]\\n\"\n      << \"ldr x20, [x1, #\" << offsetof(QBDI::Context, gprState.x20) << \"]\\n\"\n      << \"ldr x21, [x1, #\" << offsetof(QBDI::Context, gprState.x21) << \"]\\n\"\n      << \"ldr x22, [x1, #\" << offsetof(QBDI::Context, gprState.x22) << \"]\\n\"\n      << \"ldr x23, [x1, #\" << offsetof(QBDI::Context, gprState.x23) << \"]\\n\"\n      << \"ldr x24, [x1, #\" << offsetof(QBDI::Context, gprState.x24) << \"]\\n\"\n      << \"ldr x25, [x1, #\" << offsetof(QBDI::Context, gprState.x25) << \"]\\n\"\n      << \"ldr x26, [x1, #\" << offsetof(QBDI::Context, gprState.x26) << \"]\\n\"\n      << \"ldr x27, [x1, #\" << offsetof(QBDI::Context, gprState.x27) << \"]\\n\"\n      << \"ldr x28, [x1, #\" << offsetof(QBDI::Context, gprState.x28) << \"]\\n\"\n      << \"ldr x29, [x1, #\" << offsetof(QBDI::Context, gprState.x29) << \"]\\n\"\n      << \"ldr x30, [x1, #\" << offsetof(QBDI::Context, gprState.lr)\n      << \"]\\n\"\n      // save and replace sp\n      << \"mov x0, sp\\n\"\n      << \"str x0, [x1, #\" << offsetof(QBDI::Context, hostState.sp) << \"]\\n\"\n      << \"ldr x0, [x1, #\" << offsetof(QBDI::Context, gprState.sp) << \"]\\n\"\n      << \"mov sp, x0\\n\"\n      << \"stp xzr, x1, [sp, #-16]!\\n\" // stack must be align\n      << \"ldr x0, [x1, #\" << offsetof(QBDI::Context, gprState.x0) << \"]\\n\"\n      << \"ldr x1, [x1, #\" << offsetof(QBDI::Context, gprState.x1) << \"]\\n\";\n  finalSource << source;\n  finalSource\n      << \"str x1, [sp]\\n\"\n      << \"ldr x1, [sp, #8]\\n\"\n      << \"str x0, [x1, #\" << offsetof(QBDI::Context, gprState.x0) << \"]\\n\"\n      << \"ldp x0, x1, [sp], #16\\n\"\n      << \"str x0, [x1, #\" << offsetof(QBDI::Context, gprState.x1) << \"]\\n\"\n      << \"mrs x0, nzcv\\n\"\n      << \"str x0, [x1, #\" << offsetof(QBDI::Context, gprState.nzcv) << \"]\\n\"\n      << \"mrs x0, fpcr\\n\"\n      << \"str x0, [x1, #\" << offsetof(QBDI::Context, fprState.fpcr) << \"]\\n\"\n      << \"mrs x0, fpsr\\n\"\n      << \"str x0, [x1, #\" << offsetof(QBDI::Context, fprState.fpsr) << \"]\\n\"\n      << \"str x2, [x1, #\" << offsetof(QBDI::Context, gprState.x2) << \"]\\n\"\n      << \"str x3, [x1, #\" << offsetof(QBDI::Context, gprState.x3) << \"]\\n\"\n      << \"str x4, [x1, #\" << offsetof(QBDI::Context, gprState.x4) << \"]\\n\"\n      << \"str x5, [x1, #\" << offsetof(QBDI::Context, gprState.x5) << \"]\\n\"\n      << \"str x6, [x1, #\" << offsetof(QBDI::Context, gprState.x6) << \"]\\n\"\n      << \"str x7, [x1, #\" << offsetof(QBDI::Context, gprState.x7) << \"]\\n\"\n      << \"str x8, [x1, #\" << offsetof(QBDI::Context, gprState.x8) << \"]\\n\"\n      << \"str x9, [x1, #\" << offsetof(QBDI::Context, gprState.x9) << \"]\\n\"\n      << \"str x10, [x1, #\" << offsetof(QBDI::Context, gprState.x10) << \"]\\n\"\n      << \"str x11, [x1, #\" << offsetof(QBDI::Context, gprState.x11) << \"]\\n\"\n      << \"str x12, [x1, #\" << offsetof(QBDI::Context, gprState.x12) << \"]\\n\"\n      << \"str x13, [x1, #\" << offsetof(QBDI::Context, gprState.x13) << \"]\\n\"\n      << \"str x14, [x1, #\" << offsetof(QBDI::Context, gprState.x14) << \"]\\n\"\n      << \"str x15, [x1, #\" << offsetof(QBDI::Context, gprState.x15) << \"]\\n\"\n      << \"str x16, [x1, #\" << offsetof(QBDI::Context, gprState.x16) << \"]\\n\"\n      << \"str x17, [x1, #\" << offsetof(QBDI::Context, gprState.x17) << \"]\\n\"\n#if not(defined(QBDI_PLATFORM_MACOS) || defined(QBDI_PLATFORM_IOS))\n      << \"str x18, [x1, #\" << offsetof(QBDI::Context, gprState.x18) << \"]\\n\"\n#endif\n      << \"str x19, [x1, #\" << offsetof(QBDI::Context, gprState.x19) << \"]\\n\"\n      << \"str x20, [x1, #\" << offsetof(QBDI::Context, gprState.x20) << \"]\\n\"\n      << \"str x21, [x1, #\" << offsetof(QBDI::Context, gprState.x21) << \"]\\n\"\n      << \"str x22, [x1, #\" << offsetof(QBDI::Context, gprState.x22) << \"]\\n\"\n      << \"str x23, [x1, #\" << offsetof(QBDI::Context, gprState.x23) << \"]\\n\"\n      << \"str x24, [x1, #\" << offsetof(QBDI::Context, gprState.x24) << \"]\\n\"\n      << \"str x25, [x1, #\" << offsetof(QBDI::Context, gprState.x25) << \"]\\n\"\n      << \"str x26, [x1, #\" << offsetof(QBDI::Context, gprState.x26) << \"]\\n\"\n      << \"str x27, [x1, #\" << offsetof(QBDI::Context, gprState.x27) << \"]\\n\"\n      << \"str x28, [x1, #\" << offsetof(QBDI::Context, gprState.x28) << \"]\\n\"\n      << \"str x29, [x1, #\" << offsetof(QBDI::Context, gprState.x29) << \"]\\n\"\n      << \"str x30, [x1, #\" << offsetof(QBDI::Context, gprState.lr)\n      << \"]\\n\"\n      // restore sp\n      << \"ldr x0, [x1, #\" << offsetof(QBDI::Context, hostState.sp) << \"]\\n\"\n      << \"mov sp, x0\\n\"\n      << \"add x1, x1, #\" << offsetof(QBDI::Context, fprState.v0) << \"\\n\";\n  for (uint32_t i = 0; i < QBDI_NUM_FPR; i = i + 4) {\n    finalSource << \"st1 {v\" << i << \".2d-v\" << i + 3 << \".2d}, [x1], #\"\n                << sizeof(__uint128_t) * 4 << \"\\n\";\n  }\n  finalSource << \"ldp x29, lr, [sp], 16\\n\";\n  finalSource << \"ret\\n\";\n\n  return InMemoryObject(finalSource.str().c_str(), CPU, \"aarch64\", MATTRS);\n}\n\nQBDI::Context ComparedExecutor_AARCH64::jitExec(llvm::ArrayRef<uint8_t> code,\n                                                QBDI::Context &inputState,\n                                                llvm::sys::MemoryBlock &stack) {\n  QBDI::Context outputState;\n  QBDI::Context outerState;\n  llvm::sys::MemoryBlock ctxBlock;\n  llvm::sys::MemoryBlock outerStack;\n  std::error_code ec;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  outerStack = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  memset((void *)&outerState, 0, sizeof(QBDI::Context));\n  // Put the inputState on the stack\n  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  memcpy((void *)ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Prepare the outerState to fake the realExec() action\n  outerState.gprState.sp =\n      (QBDI::rword)outerStack.base() + outerStack.allocatedSize();\n  outerState.gprState.lr = (QBDI::rword)0;\n  outerState.gprState.x1 = (QBDI::rword)ctxBlock.base();\n\n  vm.setGPRState(&outerState.gprState);\n  vm.setFPRState(&outerState.fprState);\n  vm.addInstrumentedRange((QBDI::rword)code.data(),\n                          (QBDI::rword)code.data() + code.size());\n  vm.run((QBDI::rword)code.data(), 0);\n  vm.removeInstrumentedRange((QBDI::rword)code.data(),\n                             (QBDI::rword)code.data() + code.size());\n\n  memcpy((void *)&outputState, (void *)ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n  llvm::sys::Memory::releaseMappedMemory(outerStack);\n\n  return outputState;\n}\n\nQBDI::Context\nComparedExecutor_AARCH64::realExec(llvm::ArrayRef<uint8_t> code,\n                                   QBDI::Context &inputState,\n                                   llvm::sys::MemoryBlock &stack) {\n\n  QBDI::Context outputState;\n  std::error_code ec;\n  llvm::sys::MemoryBlock ctxBlock;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n\n  // Put the inputState on the stack\n  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  // Assemble the sources\n  // Copy the input context\n  memcpy(ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Execute\n  asm volatile inline(\n      \"mov x1, %1\\n\"\n      \"mov x2, %0\\n\"\n      \"stp x0, lr, [SP, #-16]!\\n\"\n      \"blr x2\\n\"\n      \"ldp x0, lr, [SP], #16\\n\"\n      :\n      : \"r\"(code.data()), \"r\"(ctxBlock.base())\n      : \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\",\n        \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x19\", \"x20\", \"x21\", \"x22\",\n        \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"v0\", \"v1\", \"v2\", \"v3\", \"v4\",\n        \"v5\", \"v6\", \"v7\", \"v8\", \"v9\", \"v10\", \"v11\", \"v12\", \"v13\", \"v14\", \"v15\",\n        \"v16\", \"v17\", \"v18\", \"v19\", \"v20\", \"v21\", \"v22\", \"v23\", \"v24\", \"v25\",\n        \"v26\", \"v27\", \"v28\", \"v29\", \"v30\", \"v31\",\n#if not(defined(QBDI_PLATFORM_MACOS) || defined(QBDI_PLATFORM_IOS))\n        \"x18\",\n#endif\n        \"memory\");\n  // Get the output context\n  memcpy((void *)&outputState, ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n\n  return outputState;\n}\n\nvoid ComparedExecutor_AARCH64::initContext(QBDI::Context &ctx) {\n  memset(&ctx, 0, sizeof(QBDI::Context));\n  ctx.gprState.x0 = get_random();\n  ctx.gprState.x1 = get_random();\n  ctx.gprState.x2 = get_random();\n  ctx.gprState.x3 = get_random();\n  ctx.gprState.x4 = get_random();\n  ctx.gprState.x5 = get_random();\n  ctx.gprState.x6 = get_random();\n  ctx.gprState.x7 = get_random();\n  ctx.gprState.x8 = get_random();\n  ctx.gprState.x9 = get_random();\n  ctx.gprState.x10 = get_random();\n  ctx.gprState.x11 = get_random();\n  ctx.gprState.x12 = get_random();\n  ctx.gprState.x13 = get_random();\n  ctx.gprState.x14 = get_random();\n  ctx.gprState.x15 = get_random();\n  ctx.gprState.x16 = get_random();\n  ctx.gprState.x17 = get_random();\n  ctx.gprState.x18 = get_random();\n  ctx.gprState.x19 = get_random();\n  ctx.gprState.x20 = get_random();\n  ctx.gprState.x21 = get_random();\n  ctx.gprState.x22 = get_random();\n  ctx.gprState.x23 = get_random();\n  ctx.gprState.x24 = get_random();\n  ctx.gprState.x25 = get_random();\n  ctx.gprState.x26 = get_random();\n  ctx.gprState.x27 = get_random();\n  ctx.gprState.x28 = get_random();\n  ctx.gprState.x29 = get_random();\n  ctx.gprState.lr = get_random();\n}\n\nconst char *GPRSave_s =\n    \"    mov x0, #1\\n\"\n    \"    mov x1, #2\\n\"\n    \"    mov x2, #3\\n\"\n    \"    mov x3, #4\\n\"\n    \"    mov x4, #5\\n\"\n    \"    mov x5, #6\\n\"\n    \"    mov x6, #7\\n\"\n    \"    mov x7, #8\\n\"\n    \"    mov x8, #9\\n\"\n    \"    mov x9, #10\\n\"\n    \"    mov x10, #11\\n\"\n    \"    mov x11, #12\\n\"\n    \"    mov x12, #13\\n\"\n    \"    mov x13, #14\\n\"\n    \"    mov x14, #15\\n\"\n    \"    mov x15, #16\\n\"\n    \"    mov x16, #17\\n\"\n    \"    mov x17, #18\\n\"\n#if not(defined(QBDI_PLATFORM_MACOS) || defined(QBDI_PLATFORM_IOS))\n    \"    mov x18, #19\\n\"\n#endif\n    \"    mov x19, #20\\n\"\n    \"    mov x20, #21\\n\"\n    \"    mov x21, #22\\n\"\n    \"    mov x22, #23\\n\"\n    \"    mov x23, #24\\n\"\n    \"    mov x24, #25\\n\"\n    \"    mov x25, #26\\n\"\n    \"    mov x26, #27\\n\"\n    \"    mov x27, #28\\n\"\n    \"    mov x28, #29\\n\"\n    \"    mov x29, #30\\n\";\n\nconst char *GPRShuffle_s =\n    \"    stp x0,  x1,   [sp, #-16]!\\n\"\n    \"    stp x2,  x3,   [sp, #-16]!\\n\"\n    \"    stp x4,  x5,   [sp, #-16]!\\n\"\n    \"    stp x6,  x7,   [sp, #-16]!\\n\"\n    \"    stp x8,  x9,   [sp, #-16]!\\n\"\n    \"    stp x10, x11,  [sp, #-16]!\\n\"\n    \"    stp x12, x13,  [sp, #-16]!\\n\"\n    \"    stp x14, x15,  [sp, #-16]!\\n\"\n    \"    stp x16, x17,  [sp, #-16]!\\n\"\n#if not(defined(QBDI_PLATFORM_MACOS) || defined(QBDI_PLATFORM_IOS))\n    \"    stp x18, x19,  [sp, #-16]!\\n\"\n#else\n    \"    stp xzr, x19,  [sp, #-16]!\\n\"\n#endif\n    \"    stp x20, x21,  [sp, #-16]!\\n\"\n    \"    stp x22, x23,  [sp, #-16]!\\n\"\n    \"    stp x24, x25,  [sp, #-16]!\\n\"\n    \"    stp x26, x27,  [sp, #-16]!\\n\"\n    \"    stp x28, x29,  [sp, #-16]!\\n\"\n    \"    ldp x1,  x0,   [sp], 16\\n\"\n    \"    ldp x21, x20,  [sp], 16\\n\"\n    \"    ldp x5,  x11,  [sp], 16\\n\"\n    \"    ldp x10, x27,  [sp], 16\\n\"\n    \"    ldp x9,  x28,  [sp], 16\\n\"\n    \"    ldp x25, x3,   [sp], 16\\n\"\n    \"    ldp x4,  x22,  [sp], 16\\n\"\n    \"    ldp x26, x6,   [sp], 16\\n\"\n    \"    ldp x24, x2,   [sp], 16\\n\"\n    \"    ldp x17, x29,  [sp], 16\\n\"\n    \"    ldp x15, x13,  [sp], 16\\n\"\n    \"    ldp x8,  x12,  [sp], 16\\n\"\n    \"    ldp x23, x14,  [sp], 16\\n\"\n    \"    ldp x19, x7,   [sp], 16\\n\"\n#if not(defined(QBDI_PLATFORM_MACOS) || defined(QBDI_PLATFORM_IOS))\n    \"    ldp x16, x18,  [sp], 16\\n\";\n#else\n    \"    ldp x16, xzr,  [sp], 16\\n\";\n#endif\n\nconst char *RelativeAddressing_s =\n    \"b start\\n\"\n    \"c1:\\n\"\n    \"    .quad 0x123456789abcdef0\\n\"\n    \"start:\\n\"\n    \"    adr x4, c1\\n\"\n    \"    ldr x5, [x4]\\n\"\n    \"    eor x0, x0, x5\\n\"\n    \"    adr x6, c2\\n\"\n    \"    ldr x7, [x6]\\n\"\n    \"    eor x1, x1, x7\\n\"\n    \"    b end\\n\"\n    \"c2:\\n\"\n    \"    .quad 0x0fedcba987654321\\n\"\n    \"end:\\n\";\n\nconst char *ConditionalBranching_s =\n    \"    stp x2,  x3,   [sp, #-16]!\\n\"\n    \"    stp x0,  x1,   [sp, #-16]!\\n\"\n    \"    mov x12, #0\\n\"\n    \"    mov x2, #0\\n\"\n    \"    mov x1, #0\\n\"\n    \"    mov x0, sp\\n\"\n    \"loop:\\n\"\n    \"    ldrb w1, [x0], 1\\n\"\n    \"    eor x12, x12, x1\\n\"\n    \"    ror x12, x12, #12\\n\"\n    \"    add x2, x2, #1\\n\"\n    \"    cmp x2, #32\\n\"\n    \"    blt loop\\n\"\n    \"    adr x3, checksum\\n\"\n    \"    ldr x4, [x3]\\n\"\n    \"    cmp x4, x2\\n\"\n    \"    bne bad\\n\"\n    \"    mov x0, #1\\n\"\n    \"    b end\\n\"\n    \"bad:\\n\"\n    \"    mov x0, #0\\n\"\n    \"    b end\\n\"\n    \"checksum:\\n\"\n    \"    .quad 0x32253676ffe8dd7f\\n\"\n    \"end:\\n\"\n    \"    add sp, sp, 32\\n\";\n\nconst char *FibonacciRecursion_s =\n    \"    adr x1, fibo\\n\"\n    \"    stp x1, x28, [sp, #-16]!\\n\"\n    \"    blr x1\\n\"\n    \"    b end\\n\"\n    \"fibo:\\n\"\n    \"    stp lr, x0, [sp, #-16]!\\n\"\n    \"    cmp x0, #2\\n\"\n    \"    bhi fibo1\\n\"\n    \"    mov x0, #1\\n\"\n    \"    b fibo2\\n\"\n    \"fibo1:\\n\"\n    \"    sub x0, x0, #1\\n\"\n    \"    str x0, [sp, #8]\\n\"\n    \"    bl fibo\\n\"\n    \"    ldr x2, [sp, #8]\\n\"\n    \"    str x0, [sp, #8]\\n\"\n    \"    sub x0, x2, #1\\n\"\n    \"    adr x1, fibo\\n\"\n    \"    blr x1\\n\"\n    \"    ldr x1, [sp, #8]\\n\"\n    \"    add x0, x0, x1\\n\"\n    \"fibo2:\\n\"\n    \"    ldp lr, x1, [sp], 16\\n\"\n    \"    ret\\n\"\n    \"end:\\n\"\n    \"    ldp x1, x28, [sp], 16\\n\";\n\nconst char *StackTricks_s =\n    \"    adr x2, end\\n\"\n    \"    adr x3, f1\\n\"\n    \"    stp x2, x0, [sp, #-16]!\\n\"\n    \"    mov lr, x3\\n\"\n    \"    ret\\n\"\n    \"f1:\\n\"\n    \"    ldr x0, [sp, #8]\\n\"\n    \"    adr x2, f2\\n\"\n    \"    adr x3, f6\\n\"\n    \"    mov x4, #1\\n\"\n    \"    cmp x0, #2\\n\"\n    \"    csel x3, x2, x3, hi\\n\"\n    \"    mov lr, x3\\n\"\n    \"    ret\\n\"\n    \"f2:\\n\"\n    \"    sub x0, x0, #1\\n\"\n    \"    adr x2, f4\\n\"\n    \"    adr x3, f1\\n\"\n    \"    stp x2, x0, [sp, #-16]!\\n\"\n    \"    mov lr, x3\\n\"\n    \"    ret\\n\"\n    \"f4:\\n\"\n    \"    add x1, x1, x4\\n\"\n    \"    sub x0, x0, #1\\n\"\n    \"    adr x2, f5\\n\"\n    \"    adr x3, f1\\n\"\n    \"    stp x2, x0, [sp, #-16]!\\n\"\n    \"    mov lr, x3\\n\"\n    \"    ret\\n\"\n    \"f5:\\n\"\n    \"    add x4, x4, x1\\n\"\n    \"f6:\\n\"\n    \"    add x0, sp, #8\\n\"\n    \"    ldp lr, x0, [sp], 16\\n\"\n    \"    ret\\n\"\n    \"end:\\n\";\n"
  },
  {
    "path": "test/Patch/AARCH64/ComparedExecutor_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef COMPAREDEXECUTOR_AARCH64_H\n#define COMPAREDEXECUTOR_AARCH64_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n#include \"TestSetup/ShellcodeTester.h\"\n\n#define CPU_CPU \"cortex-a57\"\n#define CPU_MATTRS \"neon\"\n\nextern const char *GPRSave_s;\nextern const char *GPRShuffle_s;\nextern const char *RelativeAddressing_s;\nextern const char *ConditionalBranching_s;\nextern const char *FibonacciRecursion_s;\nextern const char *StackTricks_s;\n\nclass ComparedExecutor_AARCH64 : public ShellcodeTester {\n\npublic:\n  ComparedExecutor_AARCH64() : ShellcodeTester(CPU_CPU, {CPU_MATTRS}) {}\n\n  QBDI::Context jitExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                        llvm::sys::MemoryBlock &stack);\n\n  QBDI::Context realExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                         llvm::sys::MemoryBlock &stack);\n\n  InMemoryObject compileWithContextSwitch(const char *source);\n\n  void initContext(QBDI::Context &ctx);\n};\n\n#endif\n"
  },
  {
    "path": "test/Patch/AARCH64/LLVMOperandInfo_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <set>\n\n#include <catch2/catch_test_macros.hpp>\n#include <stdio.h>\n\n#include \"AArch64InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"TestSetup/LLVMTestEnv.h\"\n#include \"Patch/InstInfo.h\"\n\nclass LLVMOperandInfoCheck : public LLVMTestEnv {\nprotected:\n  void checkTIEDOperand();\n};\n\n// LLVM MCInstrDesc specify some operand are TIED_TO another one. We want to\n// verify the followed properties:\n//\n// - If the operation is variadic, the last operand isn't TIED_TO another one.\n// - Two operands can't be TIED_TO to the same operand\n// - An operand is TIED_TO an previous operand\n//\n// An instruction with TIED_TO operand must be in one of theses two cases:\n// - The operand that is tied are the first in the list, and no futher operand.\n//    -> In this case, the instAnalysis skip the tieded operand\n// - Each tied operand is tied to the previous operand.\n//    -> In this case, the instAnalysis merge the operand with the previous one.\n\nvoid LLVMOperandInfoCheck::checkTIEDOperand() {\n\n  const llvm::MCInstrInfo &MCII = getCPU(QBDI::CPUMode::DEFAULT).getMCII();\n\n  for (unsigned opcode = 0; opcode < llvm::AArch64::INSTRUCTION_LIST_END;\n       opcode++) {\n\n    const llvm::MCInstrDesc &desc = MCII.get(opcode);\n    const char *mnemonic = MCII.getName(opcode).data();\n    const int numOperand = desc.getNumOperands();\n\n    // the opcode is a pseudo instruction used by LLVM internally\n    if (desc.isPseudo())\n      continue;\n\n    if (desc.isVariadic()) {\n      if (numOperand == 0) {\n        FAIL_CHECK(\"Instruction \"\n                   << mnemonic << \" is variadic but doesn't have any operand.\");\n      }\n      if (desc.getOperandConstraint(numOperand - 1, llvm::MCOI::TIED_TO) !=\n          -1) {\n        FAIL_CHECK(\n            \"Instruction \"\n            << mnemonic\n            << \" is variadic but the last operand is tied to another one.\");\n      }\n      for (int opn = 0; opn < numOperand; opn++) {\n        if (desc.getOperandConstraint(opn, llvm::MCOI::TIED_TO) ==\n            numOperand - 1) {\n          FAIL_CHECK(\"Instruction \" << mnemonic\n                                    << \" is variadic but the operand \" << opn\n                                    << \"is tied to the variadic operand.\");\n        }\n      }\n    }\n\n    bool tiedtoPreviousOperand = true;\n    int numTied = 0;\n\n    for (int opn = 0; opn < numOperand; opn++) {\n      int opnTied = desc.getOperandConstraint(opn, llvm::MCOI::TIED_TO);\n      if (opnTied != -1) {\n        numTied++;\n        if (opnTied >= opn) {\n          FAIL_CHECK(\"Instruction \" << mnemonic << \" has the operand \" << opn\n                                    << \" tied to the next operand \" << opnTied\n                                    << \".\");\n        }\n        if (opnTied != opn - 1) {\n          tiedtoPreviousOperand = false;\n        }\n\n        for (int opn2 = opn + 1; opn2 < numOperand; opn2++) {\n          int opnTied2 = desc.getOperandConstraint(opn2, llvm::MCOI::TIED_TO);\n          if (opnTied == opnTied2) {\n            FAIL_CHECK(\"Instruction \" << mnemonic << \" has operands \" << opn\n                                      << \" and \" << opn2\n                                      << \" both tied to the same operand\");\n          }\n        }\n      }\n    }\n\n    int sequencTied = 0;\n    for (int opn = 0; opn < numTied; opn++) {\n      bool found = false;\n      for (int op = opn + 1; op < numOperand; op++) {\n        if (desc.getOperandConstraint(op, llvm::MCOI::TIED_TO) == opn) {\n          sequencTied++;\n          found = true;\n          break;\n        }\n      }\n      if (not found) {\n        break;\n      }\n    }\n\n    if ((sequencTied != numTied) and (not tiedtoPreviousOperand)) {\n      FAIL_CHECK(\"Instruction \"\n                 << mnemonic\n                 << \" fail to verify Bias or Previous Operand property.\");\n    }\n  }\n}\n\nTEST_CASE_METHOD(LLVMOperandInfoCheck, \"LLVMOperandInfo-CrossCheck\") {\n  checkTIEDOperand();\n}\n"
  },
  {
    "path": "test/Patch/AARCH64/MemoryAccessTable_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <set>\n\n#include <catch2/catch_test_macros.hpp>\n#include <stdio.h>\n\n#include \"AArch64InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"Patch/InstInfo.h\"\n#include \"Patch/MemoryAccessTable.h\"\n\n#define MAXFAIL 2000\n\nnamespace {\n\nusing namespace llvm::AArch64;\n\nconst std::set<unsigned> unsupportedInst{\n    // clang-format off\n    // PseudoInst\n\n    // ARMv8 SME\n    EXTRACT_ZPMXI_H_B,\n    EXTRACT_ZPMXI_H_D,\n    EXTRACT_ZPMXI_H_H,\n    EXTRACT_ZPMXI_H_Q,\n    EXTRACT_ZPMXI_H_S,\n    EXTRACT_ZPMXI_V_B,\n    EXTRACT_ZPMXI_V_D,\n    EXTRACT_ZPMXI_V_H,\n    EXTRACT_ZPMXI_V_Q,\n    EXTRACT_ZPMXI_V_S,\n    LD1B_2Z_STRIDED,\n    LD1B_2Z_STRIDED_IMM,\n    LD1B_4Z_STRIDED,\n    LD1B_4Z_STRIDED_IMM,\n    LD1D_2Z_STRIDED,\n    LD1D_2Z_STRIDED_IMM,\n    LD1D_4Z_STRIDED,\n    LD1D_4Z_STRIDED_IMM,\n    LD1H_2Z_STRIDED,\n    LD1H_2Z_STRIDED_IMM,\n    LD1H_4Z_STRIDED,\n    LD1H_4Z_STRIDED_IMM,\n    LD1W_2Z_STRIDED,\n    LD1W_2Z_STRIDED_IMM,\n    LD1W_4Z_STRIDED,\n    LD1W_4Z_STRIDED_IMM,\n    LD1_MXIPXX_H_B,\n    LD1_MXIPXX_H_D,\n    LD1_MXIPXX_H_H,\n    LD1_MXIPXX_H_Q,\n    LD1_MXIPXX_H_S,\n    LD1_MXIPXX_V_B,\n    LD1_MXIPXX_V_D,\n    LD1_MXIPXX_V_H,\n    LD1_MXIPXX_V_Q,\n    LD1_MXIPXX_V_S,\n    LDR_ZA,\n    ST1_MXIPXX_H_B,\n    ST1_MXIPXX_H_D,\n    ST1_MXIPXX_H_H,\n    ST1_MXIPXX_H_Q,\n    ST1_MXIPXX_H_S,\n    ST1_MXIPXX_V_B,\n    ST1_MXIPXX_V_D,\n    ST1_MXIPXX_V_H,\n    ST1_MXIPXX_V_Q,\n    ST1_MXIPXX_V_S,\n    STR_ZA,\n\n    // ARMv8 SVE\n    GLD1B_D,\n    GLD1B_D_IMM,\n    GLD1B_D_SXTW,\n    GLD1B_D_UXTW,\n    GLD1B_S_IMM,\n    GLD1B_S_SXTW,\n    GLD1B_S_UXTW,\n    GLD1D,\n    GLD1D_IMM,\n    GLD1D_SCALED,\n    GLD1D_SXTW,\n    GLD1D_SXTW_SCALED,\n    GLD1D_UXTW,\n    GLD1D_UXTW_SCALED,\n    GLD1H_D,\n    GLD1H_D_IMM,\n    GLD1H_D_SCALED,\n    GLD1H_D_SXTW,\n    GLD1H_D_SXTW_SCALED,\n    GLD1H_D_UXTW,\n    GLD1H_D_UXTW_SCALED,\n    GLD1H_S_IMM,\n    GLD1H_S_SXTW,\n    GLD1H_S_SXTW_SCALED,\n    GLD1H_S_UXTW,\n    GLD1H_S_UXTW_SCALED,\n    GLD1Q,\n    GLD1SB_D,\n    GLD1SB_D_IMM,\n    GLD1SB_D_SXTW,\n    GLD1SB_D_UXTW,\n    GLD1SB_S_IMM,\n    GLD1SB_S_SXTW,\n    GLD1SB_S_UXTW,\n    GLD1SH_D,\n    GLD1SH_D_IMM,\n    GLD1SH_D_SCALED,\n    GLD1SH_D_SXTW,\n    GLD1SH_D_SXTW_SCALED,\n    GLD1SH_D_UXTW,\n    GLD1SH_D_UXTW_SCALED,\n    GLD1SH_S_IMM,\n    GLD1SH_S_SXTW,\n    GLD1SH_S_SXTW_SCALED,\n    GLD1SH_S_UXTW,\n    GLD1SH_S_UXTW_SCALED,\n    GLD1SW_D,\n    GLD1SW_D_IMM,\n    GLD1SW_D_SCALED,\n    GLD1SW_D_SXTW,\n    GLD1SW_D_SXTW_SCALED,\n    GLD1SW_D_UXTW,\n    GLD1SW_D_UXTW_SCALED,\n    GLD1W_D,\n    GLD1W_D_IMM,\n    GLD1W_D_SCALED,\n    GLD1W_D_SXTW,\n    GLD1W_D_SXTW_SCALED,\n    GLD1W_D_UXTW,\n    GLD1W_D_UXTW_SCALED,\n    GLD1W_IMM,\n    GLD1W_SXTW,\n    GLD1W_SXTW_SCALED,\n    GLD1W_UXTW,\n    GLD1W_UXTW_SCALED,\n    GLDFF1B_D,\n    GLDFF1B_D_IMM,\n    GLDFF1B_D_SXTW,\n    GLDFF1B_D_UXTW,\n    GLDFF1B_S_IMM,\n    GLDFF1B_S_SXTW,\n    GLDFF1B_S_UXTW,\n    GLDFF1D,\n    GLDFF1D_IMM,\n    GLDFF1D_SCALED,\n    GLDFF1D_SXTW,\n    GLDFF1D_SXTW_SCALED,\n    GLDFF1D_UXTW,\n    GLDFF1D_UXTW_SCALED,\n    GLDFF1H_D,\n    GLDFF1H_D_IMM,\n    GLDFF1H_D_SCALED,\n    GLDFF1H_D_SXTW,\n    GLDFF1H_D_SXTW_SCALED,\n    GLDFF1H_D_UXTW,\n    GLDFF1H_D_UXTW_SCALED,\n    GLDFF1H_S_IMM,\n    GLDFF1H_S_SXTW,\n    GLDFF1H_S_SXTW_SCALED,\n    GLDFF1H_S_UXTW,\n    GLDFF1H_S_UXTW_SCALED,\n    GLDFF1SB_D,\n    GLDFF1SB_D_IMM,\n    GLDFF1SB_D_SXTW,\n    GLDFF1SB_D_UXTW,\n    GLDFF1SB_S_IMM,\n    GLDFF1SB_S_SXTW,\n    GLDFF1SB_S_UXTW,\n    GLDFF1SH_D,\n    GLDFF1SH_D_IMM,\n    GLDFF1SH_D_SCALED,\n    GLDFF1SH_D_SXTW,\n    GLDFF1SH_D_SXTW_SCALED,\n    GLDFF1SH_D_UXTW,\n    GLDFF1SH_D_UXTW_SCALED,\n    GLDFF1SH_S_IMM,\n    GLDFF1SH_S_SXTW,\n    GLDFF1SH_S_SXTW_SCALED,\n    GLDFF1SH_S_UXTW,\n    GLDFF1SH_S_UXTW_SCALED,\n    GLDFF1SW_D,\n    GLDFF1SW_D_IMM,\n    GLDFF1SW_D_SCALED,\n    GLDFF1SW_D_SXTW,\n    GLDFF1SW_D_SXTW_SCALED,\n    GLDFF1SW_D_UXTW,\n    GLDFF1SW_D_UXTW_SCALED,\n    GLDFF1W_D,\n    GLDFF1W_D_IMM,\n    GLDFF1W_D_SCALED,\n    GLDFF1W_D_SXTW,\n    GLDFF1W_D_SXTW_SCALED,\n    GLDFF1W_D_UXTW,\n    GLDFF1W_D_UXTW_SCALED,\n    GLDFF1W_IMM,\n    GLDFF1W_SXTW,\n    GLDFF1W_SXTW_SCALED,\n    GLDFF1W_UXTW,\n    GLDFF1W_UXTW_SCALED,\n    LD1B,\n    LD1B_2Z,\n    LD1B_2Z_IMM,\n    LD1B_4Z,\n    LD1B_4Z_IMM,\n    LD1B_D,\n    LD1B_D_IMM,\n    LD1B_H,\n    LD1B_H_IMM,\n    LD1B_IMM,\n    LD1B_S,\n    LD1B_S_IMM,\n    LD1D,\n    LD1D_2Z,\n    LD1D_2Z_IMM,\n    LD1D_4Z,\n    LD1D_4Z_IMM,\n    LD1D_IMM,\n    LD1D_Q,\n    LD1D_Q_IMM,\n    LD1H,\n    LD1H,\n    LD1H_2Z,\n    LD1H_2Z_IMM,\n    LD1H_4Z,\n    LD1H_4Z_IMM,\n    LD1H_D,\n    LD1H_D,\n    LD1H_D_IMM,\n    LD1H_D_IMM,\n    LD1H_IMM,\n    LD1H_IMM,\n    LD1H_S,\n    LD1H_S,\n    LD1H_S_IMM,\n    LD1H_S_IMM,\n    LD1RB_D_IMM,\n    LD1RB_H_IMM,\n    LD1RB_IMM,\n    LD1RB_S_IMM,\n    LD1RD_IMM,\n    LD1RH_D_IMM,\n    LD1RH_IMM,\n    LD1RH_IMM,\n    LD1RH_S_IMM,\n    LD1RH_S_IMM,\n    LD1RO_B,\n    LD1RO_B_IMM,\n    LD1RO_D,\n    LD1RO_D_IMM,\n    LD1RO_H,\n    LD1RO_H_IMM,\n    LD1RO_W,\n    LD1RO_W_IMM,\n    LD1RQ_B,\n    LD1RQ_B_IMM,\n    LD1RQ_D,\n    LD1RQ_D_IMM,\n    LD1RQ_H,\n    LD1RQ_H_IMM,\n    LD1RQ_W,\n    LD1RQ_W_IMM,\n    LD1RSB_D_IMM,\n    LD1RSB_H_IMM,\n    LD1RSB_S_IMM,\n    LD1RSH_D_IMM,\n    LD1RSH_S_IMM,\n    LD1RSW_IMM,\n    LD1RW_D_IMM,\n    LD1RW_IMM,\n    LD1SB_D,\n    LD1SB_D_IMM,\n    LD1SB_H,\n    LD1SB_H_IMM,\n    LD1SB_S,\n    LD1SB_S_IMM,\n    LD1SH_D,\n    LD1SH_D_IMM,\n    LD1SH_S,\n    LD1SH_S_IMM,\n    LD1SW_D,\n    LD1SW_D_IMM,\n    LD1W,\n    LD1W_2Z,\n    LD1W_2Z_IMM,\n    LD1W_4Z,\n    LD1W_4Z_IMM,\n    LD1W_D,\n    LD1W_D_IMM,\n    LD1W_IMM,\n    LD1W_Q,\n    LD1W_Q_IMM,\n    LD2B,\n    LD2B_IMM,\n    LD2D,\n    LD2D_IMM,\n    LD2H,\n    LD2H_IMM,\n    LD2Q,\n    LD2Q_IMM,\n    LD2W,\n    LD2W_IMM,\n    LD3B,\n    LD3B_IMM,\n    LD3D,\n    LD3D_IMM,\n    LD3H,\n    LD3H_IMM,\n    LD3Q,\n    LD3Q_IMM,\n    LD3W,\n    LD3W_IMM,\n    LD4B,\n    LD4B_IMM,\n    LD4D,\n    LD4D_IMM,\n    LD4H,\n    LD4H_IMM,\n    LD4Q,\n    LD4Q_IMM,\n    LD4W,\n    LD4W_IMM,\n    LDFF1B,\n    LDFF1B_D,\n    LDFF1B_H,\n    LDFF1B_S,\n    LDFF1D,\n    LDFF1H,\n    LDFF1H_D,\n    LDFF1H_S,\n    LDFF1SB_D,\n    LDFF1SB_H,\n    LDFF1SB_S,\n    LDFF1SH_D,\n    LDFF1SH_S,\n    LDFF1SW_D,\n    LDFF1W,\n    LDFF1W_D,\n    LDNF1B_D_IMM,\n    LDNF1B_H_IMM,\n    LDNF1B_IMM,\n    LDNF1B_S_IMM,\n    LDNF1D_IMM,\n    LDNF1H_D_IMM,\n    LDNF1H_IMM,\n    LDNF1H_S_IMM,\n    LDNF1SB_D_IMM,\n    LDNF1SB_H_IMM,\n    LDNF1SB_S_IMM,\n    LDNF1SH_D_IMM,\n    LDNF1SH_S_IMM,\n    LDNF1SW_D_IMM,\n    LDNF1W_D_IMM,\n    LDNF1W_IMM,\n    LDNT1B_ZRI,\n    LDNT1B_ZRR,\n    LDNT1B_ZZR_D,\n    LDNT1B_ZZR_S,\n    LDNT1D_ZRI,\n    LDNT1D_ZRR,\n    LDNT1D_ZZR_D,\n    LDNT1H_ZRI,\n    LDNT1H_ZRR,\n    LDNT1H_ZZR_D,\n    LDNT1H_ZZR_S,\n    LDNT1SB_ZZR_D,\n    LDNT1SB_ZZR_S,\n    LDNT1SH_ZZR_D,\n    LDNT1SH_ZZR_S,\n    LDNT1SW_ZZR_D,\n    LDNT1W_ZRI,\n    LDNT1W_ZRR,\n    LDNT1W_ZZR_D,\n    LDNT1W_ZZR_S,\n    LDR_PXI,\n    LDR_ZXI,\n    PRFB_D_PZI,\n    PRFB_D_SCALED,\n    PRFB_D_SXTW_SCALED,\n    PRFB_D_UXTW_SCALED,\n    PRFB_PRI,\n    PRFB_PRR,\n    PRFB_S_PZI,\n    PRFB_S_SXTW_SCALED,\n    PRFB_S_UXTW_SCALED,\n    PRFD_D_PZI,\n    PRFD_D_SCALED,\n    PRFD_D_SXTW_SCALED,\n    PRFD_D_UXTW_SCALED,\n    PRFD_PRI,\n    PRFD_PRR,\n    PRFD_S_PZI,\n    PRFD_S_SXTW_SCALED,\n    PRFD_S_UXTW_SCALED,\n    PRFH_D_PZI,\n    PRFH_D_SCALED,\n    PRFH_D_SXTW_SCALED,\n    PRFH_D_UXTW_SCALED,\n    PRFH_PRI,\n    PRFH_PRR,\n    PRFH_S_PZI,\n    PRFH_S_SXTW_SCALED,\n    PRFH_S_UXTW_SCALED,\n    PRFW_D_PZI,\n    PRFW_D_SCALED,\n    PRFW_D_SXTW_SCALED,\n    PRFW_D_UXTW_SCALED,\n    PRFW_PRI,\n    PRFW_PRR,\n    PRFW_S_PZI,\n    PRFW_S_SXTW_SCALED,\n    PRFW_S_UXTW_SCALED,\n    PSEL_PPPRI_B,\n    PSEL_PPPRI_D,\n    PSEL_PPPRI_H,\n    PSEL_PPPRI_S,\n    SETFFR,\n    SST1B_D,\n    SST1B_D_IMM,\n    SST1B_D_SXTW,\n    SST1B_D_UXTW,\n    SST1B_S_IMM,\n    SST1B_S_SXTW,\n    SST1B_S_UXTW,\n    SST1D,\n    SST1D_IMM,\n    SST1D_SCALED,\n    SST1D_SXTW,\n    SST1D_SXTW_SCALED,\n    SST1D_UXTW,\n    SST1D_UXTW_SCALED,\n    SST1H_D,\n    SST1H_D_IMM,\n    SST1H_D_SCALED,\n    SST1H_D_SXTW,\n    SST1H_D_SXTW_SCALED,\n    SST1H_D_UXTW,\n    SST1H_D_UXTW_SCALED,\n    SST1H_S_IMM,\n    SST1H_S_SXTW,\n    SST1H_S_SXTW_SCALED,\n    SST1H_S_UXTW,\n    SST1H_S_UXTW_SCALED,\n    SST1Q,\n    SST1W_D,\n    SST1W_D_IMM,\n    SST1W_D_SCALED,\n    SST1W_D_SXTW,\n    SST1W_D_SXTW_SCALED,\n    SST1W_D_UXTW,\n    SST1W_D_UXTW_SCALED,\n    SST1W_IMM,\n    SST1W_SXTW,\n    SST1W_SXTW_SCALED,\n    SST1W_UXTW,\n    SST1W_UXTW_SCALED,\n    ST1B,\n    ST1B_D,\n    ST1B_D_IMM,\n    ST1B_H,\n    ST1B_H_IMM,\n    ST1B_IMM,\n    ST1B_S,\n    ST1B_S_IMM,\n    ST1D,\n    ST1D_IMM,\n    ST1D_Q,\n    ST1D_Q_IMM,\n    ST1H,\n    ST1H_D,\n    ST1H_D_IMM,\n    ST1H_IMM,\n    ST1H_S,\n    ST1H_S_IMM,\n    ST1W,\n    ST1W_D,\n    ST1W_D_IMM,\n    ST1W_IMM,\n    ST1W_Q,\n    ST1W_Q_IMM,\n    ST2B,\n    ST2B_IMM,\n    ST2D,\n    ST2D_IMM,\n    ST2H,\n    ST2H_IMM,\n    ST2W,\n    ST2W_IMM,\n    ST3B,\n    ST3B_IMM,\n    ST3D,\n    ST3D_IMM,\n    ST3H,\n    ST3H_IMM,\n    ST3W,\n    ST3W_IMM,\n    ST4B,\n    ST4B_IMM,\n    ST4D,\n    ST4D_IMM,\n    ST4H,\n    ST4H_IMM,\n    ST4W,\n    ST4W_IMM,\n    STNT1B_ZRI,\n    STNT1B_ZRR,\n    STNT1B_ZZR_D,\n    STNT1B_ZZR_S,\n    STNT1D_ZRI,\n    STNT1D_ZRR,\n    STNT1D_ZZR_D,\n    STNT1H_ZRI,\n    STNT1H_ZRR,\n    STNT1H_ZZR_D,\n    STNT1H_ZZR_S,\n    STNT1W_ZRI,\n    STNT1W_ZRR,\n    STNT1W_ZZR_D,\n    STNT1W_ZZR_S,\n    STR_PXI,\n    STR_ZXI,\n    WHILERW_PXX_B,\n    WHILERW_PXX_D,\n    WHILERW_PXX_H,\n    WHILERW_PXX_S,\n    WHILEWR_PXX_B,\n    WHILEWR_PXX_D,\n    WHILEWR_PXX_H,\n    WHILEWR_PXX_S,\n    WRFFR,\n\n    // ARMv8 SME2\n    LDNT1B_2Z,\n    LDNT1B_2Z_IMM,\n    LDNT1B_2Z_STRIDED,\n    LDNT1B_2Z_STRIDED_IMM,\n    LDNT1B_4Z,\n    LDNT1B_4Z_IMM,\n    LDNT1B_4Z_STRIDED,\n    LDNT1B_4Z_STRIDED_IMM,\n    LDNT1D_2Z,\n    LDNT1D_2Z_IMM,\n    LDNT1D_2Z_STRIDED,\n    LDNT1D_2Z_STRIDED_IMM,\n    LDNT1D_4Z,\n    LDNT1D_4Z_IMM,\n    LDNT1D_4Z_STRIDED,\n    LDNT1D_4Z_STRIDED_IMM,\n    LDNT1H_2Z,\n    LDNT1H_2Z_IMM,\n    LDNT1H_2Z_STRIDED,\n    LDNT1H_2Z_STRIDED_IMM,\n    LDNT1H_4Z,\n    LDNT1H_4Z_IMM,\n    LDNT1H_4Z_STRIDED,\n    LDNT1H_4Z_STRIDED_IMM,\n    LDNT1W_2Z,\n    LDNT1W_2Z_IMM,\n    LDNT1W_2Z_STRIDED,\n    LDNT1W_2Z_STRIDED_IMM,\n    LDNT1W_4Z,\n    LDNT1W_4Z_IMM,\n    LDNT1W_4Z_STRIDED,\n    LDNT1W_4Z_STRIDED_IMM,\n    LDR_TX,\n    LUTI2_ZTZI_B,\n    LUTI2_ZTZI_H,\n    LUTI2_ZTZI_S,\n    LUTI4_ZTZI_B,\n    LUTI4_ZTZI_H,\n    LUTI4_ZTZI_S,\n    ST1B_2Z,\n    ST1B_2Z_IMM,\n    ST1B_2Z_STRIDED,\n    ST1B_2Z_STRIDED_IMM,\n    ST1B_4Z,\n    ST1B_4Z_IMM,\n    ST1B_4Z_STRIDED,\n    ST1B_4Z_STRIDED_IMM,\n    ST1D_2Z,\n    ST1D_2Z_IMM,\n    ST1D_2Z_STRIDED,\n    ST1D_2Z_STRIDED_IMM,\n    ST1D_4Z,\n    ST1D_4Z_IMM,\n    ST1D_4Z_STRIDED,\n    ST1D_4Z_STRIDED_IMM,\n    ST1H_2Z,\n    ST1H_2Z_IMM,\n    ST1H_2Z_STRIDED,\n    ST1H_2Z_STRIDED_IMM,\n    ST1H_4Z,\n    ST1H_4Z_IMM,\n    ST1H_4Z_STRIDED,\n    ST1H_4Z_STRIDED_IMM,\n    ST1W_2Z,\n    ST1W_2Z_IMM,\n    ST1W_2Z_STRIDED,\n    ST1W_2Z_STRIDED_IMM,\n    ST1W_4Z,\n    ST1W_4Z_IMM,\n    ST1W_4Z_STRIDED,\n    ST1W_4Z_STRIDED_IMM,\n    ST2Q,\n    ST2Q_IMM,\n    ST3Q,\n    ST3Q_IMM,\n    ST4Q,\n    ST4Q_IMM,\n    STNT1B_2Z,\n    STNT1B_2Z_IMM,\n    STNT1B_2Z_STRIDED,\n    STNT1B_2Z_STRIDED_IMM,\n    STNT1B_4Z,\n    STNT1B_4Z_IMM,\n    STNT1B_4Z_STRIDED,\n    STNT1B_4Z_STRIDED_IMM,\n    STNT1D_2Z,\n    STNT1D_2Z_IMM,\n    STNT1D_2Z_STRIDED,\n    STNT1D_2Z_STRIDED_IMM,\n    STNT1D_4Z,\n    STNT1D_4Z_IMM,\n    STNT1D_4Z_STRIDED,\n    STNT1D_4Z_STRIDED_IMM,\n    STNT1H_2Z,\n    STNT1H_2Z_IMM,\n    STNT1H_2Z_STRIDED,\n    STNT1H_2Z_STRIDED_IMM,\n    STNT1H_4Z,\n    STNT1H_4Z_IMM,\n    STNT1H_4Z_STRIDED,\n    STNT1H_4Z_STRIDED_IMM,\n    STNT1W_2Z,\n    STNT1W_2Z_IMM,\n    STNT1W_2Z_STRIDED,\n    STNT1W_2Z_STRIDED_IMM,\n    STNT1W_4Z,\n    STNT1W_4Z_IMM,\n    STNT1W_4Z_STRIDED,\n    STNT1W_4Z_STRIDED_IMM,\n    STR_TX,\n\n    // ARMv8 MTE\n    LDG,\n    LDGM,\n    ST2GPostIndex,\n    ST2GPreIndex,\n    ST2Gi,\n    STGPi,\n    STGPostIndex,\n    STGPpost,\n    STGPpre,\n    STGPreIndex,\n    STGi,\n    STZ2GPostIndex,\n    STZ2GPreIndex,\n    STZ2Gi,\n    STZGPostIndex,\n    STZGPreIndex,\n    STZGi,\n\n    // ARMv8 GCS (Guarded Control Stack)\n    GCSPOPM,\n    GCSPUSHM,\n    GCSSS1,\n    GCSSS2,\n\n    // ARMv9 TME\n    TCANCEL,\n    TCOMMIT,\n    TSTART,\n    // clang-format on\n};\n\n// instruction that reads memory but without mayLoad\nconst std::set<unsigned> fixupRead{\n    // clang-format off\n    LD64B,\n    LDAPURSBWi,\n    LDAPURSBXi,\n    LDAPURSHWi,\n    LDAPURSHXi,\n    LDAPURSWi,\n    // clang-format on\n};\n\n// instruction that writes memory but without mayStore\nconst std::set<unsigned> fixupWrite{\n    // clang-format off\n    GCSSTR,\n    GCSSTTR,\n    ST64B,\n    ST64BV,\n    ST64BV0,\n    // clang-format on\n};\n\n// instruction with mayLoad but don't reads memory\nconst std::set<unsigned> fixupNoRead{\n    // clang-format off\n    // exclusive mecanism\n    CLREX,\n    STLXPW,\n    STLXPX,\n    STLXRB,\n    STLXRH,\n    STLXRW,\n    STLXRX,\n    STXPW,\n    STXPX,\n    STXRB,\n    STXRH,\n    STXRW,\n    STXRX,\n    // barrier\n    DMB,\n    DSB,\n    HINT,\n    ISB,\n    // other\n    RDFFR_P,\n    RDFFR_PPz,\n    // clang-format on\n};\n// instruction with mayStore but don't writes memory\nconst std::set<unsigned> fixupNoWrite{\n    // clang-format off\n    // exclusive mecanism\n    LDXRB,\n    LDXRH,\n    LDXRW,\n    LDXRX,\n    LDAXRB,\n    LDAXRH,\n    LDAXRW,\n    LDAXRX,\n    LDXPW,\n    LDXPX,\n    LDAXPW,\n    LDAXPX,\n    CLREX,\n    // barrier\n    DMB,\n    DSB,\n    HINT,\n    ISB,\n    // clang-format on\n};\n\n} // namespace\n\nTEST_CASE_METHOD(MemoryAccessTable, \"MemoryAccessTable-CrossCheck\") {\n\n  const QBDI::LLVMCPU &llvmcpu = getCPU(QBDI::CPUMode::DEFAULT);\n  const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n  int nbfail = 0;\n\n  for (unsigned opcode = 0; opcode < llvm::AArch64::INSTRUCTION_LIST_END;\n       opcode++) {\n    if (unsupportedInst.count(opcode) == 1)\n      continue;\n\n    const llvm::MCInstrDesc &desc = MCII.get(opcode);\n    const char *mnemonic = MCII.getName(opcode).data();\n\n    // InstInfo_AARCH64.cpp only use inst->getOpcode(). The MCInst doesn't need\n    // to have his operand\n    llvm::MCInst inst;\n    inst.setOpcode(opcode);\n\n    bool doRead =\n        (QBDI::getReadSize(inst, llvmcpu) != 0 or QBDI::unsupportedRead(inst));\n    bool doWrite = (QBDI::getWriteSize(inst, llvmcpu) != 0 or\n                    QBDI::unsupportedWrite(inst));\n    bool mayRead = desc.mayLoad();\n    bool mayWrite = desc.mayStore();\n\n    bool bypassRead = false;\n    bool bypassWrite = false;\n\n    // the opcode is a pseudo instruction used by LLVM internally\n    if (desc.isPseudo()) {\n      if (doRead or doWrite) {\n        WARN(\"Pseudo instruction \" << mnemonic << \" in InstInfo\");\n      }\n      continue;\n    }\n\n    // llvm mayLoad and mayStore fixup\n    if (fixupRead.count(opcode) == 1) {\n      if (doRead && !mayRead)\n        bypassRead = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupRead\");\n    }\n\n    if (fixupNoRead.count(opcode) == 1) {\n      if (!doRead && mayRead)\n        bypassRead = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupNoRead\");\n    }\n\n    if (fixupWrite.count(opcode) == 1) {\n      if (doWrite && !mayWrite)\n        bypassWrite = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupWrite\");\n    }\n\n    if (fixupNoWrite.count(opcode) == 1) {\n      if (!doWrite && mayWrite)\n        bypassWrite = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupNoWrite\");\n    }\n\n    if (!bypassRead && doRead != mayRead) {\n      if (doRead && !mayRead) {\n        FAIL_CHECK(\"Unexpected read for \" << mnemonic);\n        nbfail++;\n      } else if (!doRead && mayRead) {\n        FAIL_CHECK(\"Missing read for \" << mnemonic);\n        nbfail++;\n      }\n    }\n\n    if (!bypassWrite && doWrite != mayWrite) {\n      if (doWrite && !mayWrite) {\n        FAIL_CHECK(\"Unexpected write for \" << mnemonic);\n        nbfail++;\n      } else if (!doWrite && mayWrite) {\n        FAIL_CHECK(\"Missing write for \" << mnemonic);\n        nbfail++;\n      }\n    }\n    if (nbfail >= MAXFAIL) {\n      FAIL(\"Too many fails, abort MemoryAccessTable validation\");\n    }\n  }\n}\n"
  },
  {
    "path": "test/Patch/ARM/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/ComparedExecutor_ARM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/ComparedExecutor_Thumb.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTable_ARM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/LLVMOperandInfo_ARM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Instr_Test_ARM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Instr_Test_Thumb.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Patch_Test_ARM.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Patch_Test_Thumb.cpp\")\n"
  },
  {
    "path": "test/Patch/ARM/ComparedExecutor_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"ComparedExecutor_ARM.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"Patch/Utils.h\"\n\nconst char CPU[] = CPU_CPU;\nconst std::vector<std::string> MATTRS = {CPU_MATTRS};\n\nInMemoryObject\nComparedExecutor_ARM::compileWithContextSwitch(const char *source) {\n  std::ostringstream finalSource;\n\n  finalSource << \"push {lr}\\n\"\n              << \"add r0, r1, #\" << offsetof(QBDI::Context, fprState.vreg.d[0])\n              << \"\\n\"\n              << \"vldmia\tr0!, {d0-d15}\\n\"\n#if QBDI_NUM_FPR == 32\n              << \"vldmia\tr0, {d16-d31}\\n\"\n#endif\n              << \"ldr r0, [r1, #\" << offsetof(QBDI::Context, gprState.cpsr)\n              << \"]\\n\"\n              << \"msr APSR_nzcvqg, r0\\n\"\n              // backup SP\n              << \"str sp, [r1, #\" << offsetof(QBDI::Context, hostState.sp)\n              << \"]\\n\"\n              // set SP LR\n              << \"add r0, r1, #\" << offsetof(QBDI::Context, gprState.sp) << \"\\n\"\n              << \"ldm r0, {sp,lr}\\n\"\n              // set backup\n              << \"push {r0-r1}\\n\"\n              // set r0-r12 SP LR\n              << \"add r0, r1, #\" << offsetof(QBDI::Context, gprState.r0) << \"\\n\"\n              << \"ldm r0, {r0-r12}\\n\";\n  finalSource << source;\n  finalSource << \"str r0, [sp]\\n\"\n              << \"ldr r0, [sp, #4]\\n\"\n              // backup r1-r12 SP LR\n              << \"add r0, r0, #\" << offsetof(QBDI::Context, gprState.r1) << \"\\n\"\n              << \"stm r0, {r1-r12,sp,lr}\\n\"\n              << \"pop {r0-r1}\\n\"\n              // backup r0\n              << \"str r0, [r1, #\" << offsetof(QBDI::Context, gprState.r0)\n              << \"]\\n\"\n              // restore sp\n              << \"ldr sp, [r1, #\" << offsetof(QBDI::Context, hostState.sp)\n              << \"]\\n\"\n              << \"mrs r0, APSR\\n\"\n              << \"str r0, [r1, #\" << offsetof(QBDI::Context, gprState.cpsr)\n              << \"]\\n\"\n              << \"add r0, r1, #\" << offsetof(QBDI::Context, fprState.vreg.d[0])\n              << \"\\n\"\n              << \"vstmia  r0!, {d0-d15}\\n\"\n#if QBDI_NUM_FPR == 32\n              << \"vstmia  r0, {d16-d31}\\n\"\n#endif\n              << \"pop {pc}\\n\";\n\n  return InMemoryObject(finalSource.str().c_str(), CPU, \"arm\", MATTRS);\n}\n\nQBDI::Context ComparedExecutor_ARM::jitExec(llvm::ArrayRef<uint8_t> code,\n                                            QBDI::Context &inputState,\n                                            llvm::sys::MemoryBlock &stack) {\n  QBDI::Context outputState;\n  QBDI::Context outerState;\n  llvm::sys::MemoryBlock ctxBlock;\n  llvm::sys::MemoryBlock outerStack;\n  std::error_code ec;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  outerStack = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  memset((void *)&outerState, 0, sizeof(QBDI::Context));\n  // Put the inputState on the stack\n  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  memcpy((void *)ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Prepare the outerState to fake the realExec() action\n  outerState.gprState.sp =\n      (QBDI::rword)outerStack.base() + outerStack.allocatedSize();\n  outerState.gprState.lr = (QBDI::rword)0;\n  outerState.gprState.r1 = (QBDI::rword)ctxBlock.base();\n\n  vm.setGPRState(&outerState.gprState);\n  vm.setFPRState(&outerState.fprState);\n  vm.addInstrumentedRange((QBDI::rword)code.data(),\n                          (QBDI::rword)code.data() + code.size());\n  vm.run((QBDI::rword)code.data(), 0);\n  vm.removeInstrumentedRange((QBDI::rword)code.data(),\n                             (QBDI::rword)code.data() + code.size());\n\n  memcpy((void *)&outputState, (void *)ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n  llvm::sys::Memory::releaseMappedMemory(outerStack);\n\n  return outputState;\n}\n\nQBDI::Context ComparedExecutor_ARM::realExec(llvm::ArrayRef<uint8_t> code,\n                                             QBDI::Context &inputState,\n                                             llvm::sys::MemoryBlock &stack) {\n\n  QBDI::Context outputState;\n  std::error_code ec;\n  llvm::sys::MemoryBlock ctxBlock;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n\n  // Put the inputState on the stack\n  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  // Assemble the sources\n  // Copy the input context\n  memcpy(ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Execute\n  {\n    register uint32_t ctxBlockBase asm(\"r1\") = (uint32_t)ctxBlock.base();\n    register uint32_t codeData asm(\"r2\") = (uint32_t)code.data();\n    asm volatile inline(\n        \"push {r0, lr}\\n\"\n        \"blx r2\\n\"\n        \"pop {r0, lr}\\n\"\n        :\n        : \"r\"(codeData), \"r\"(ctxBlockBase)\n        : \"r0\", \"r3\", \"r4\", \"r5\", \"r6\", \"r7\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\",\n          \"d0\", \"d1\", \"d2\", \"d3\", \"d4\", \"d5\", \"d6\", \"d7\", \"d8\", \"d9\", \"d10\",\n          \"d11\", \"d12\", \"d13\", \"d14\", \"d15\", \"d16\", \"d17\", \"d18\", \"d19\", \"d20\",\n          \"d21\", \"d22\", \"d23\", \"d24\", \"d25\", \"d26\", \"d27\", \"d28\", \"d29\", \"d30\",\n          \"d31\", \"memory\");\n  }\n  // Get the output context\n  memcpy((void *)&outputState, ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n\n  return outputState;\n}\n\nvoid ComparedExecutor_ARM::initContext(QBDI::Context &ctx) {\n  memset(&ctx, 0, sizeof(QBDI::Context));\n  ctx.gprState.r0 = get_random();\n  ctx.gprState.r1 = get_random();\n  ctx.gprState.r2 = get_random();\n  ctx.gprState.r3 = get_random();\n  ctx.gprState.r4 = get_random();\n  ctx.gprState.r5 = get_random();\n  ctx.gprState.r6 = get_random();\n  ctx.gprState.r7 = get_random();\n  ctx.gprState.r8 = get_random();\n  ctx.gprState.r9 = get_random();\n  ctx.gprState.r10 = get_random();\n  ctx.gprState.r11 = get_random();\n  ctx.gprState.r12 = get_random();\n  ctx.gprState.lr = get_random();\n}\n\nconst char *GPRSave_s =\n    \"    mov r0, #1\\n\"\n    \"    mov r1, #2\\n\"\n    \"    mov r2, #3\\n\"\n    \"    mov r3, #4\\n\"\n    \"    mov r4, #5\\n\"\n    \"    mov r5, #6\\n\"\n    \"    mov r6, #7\\n\"\n    \"    mov r7, #8\\n\"\n    \"    mov r8, #9\\n\"\n    \"    mov r9, #10\\n\"\n    \"    mov r10, #11\\n\"\n    \"    mov r11, #12\\n\"\n    \"    mov r12, #13\\n\"\n    \"    mov lr, #14\\n\";\n\nconst char *GPRShuffle_s =\n    \"    push {r0-r12,lr}\\n\"\n    \"    pop {r3}\\n\"\n    \"    pop {r11}\\n\"\n    \"    pop {r7}\\n\"\n    \"    pop {r2}\\n\"\n    \"    pop {r12}\\n\"\n    \"    pop {r10}\\n\"\n    \"    pop {lr}\\n\"\n    \"    pop {r1}\\n\"\n    \"    pop {r5}\\n\"\n    \"    pop {r6}\\n\"\n    \"    pop {r0}\\n\"\n    \"    pop {r4}\\n\"\n    \"    pop {r9}\\n\"\n    \"    pop {r8}\\n\";\n\nconst char *RelativeAddressing_s =\n    \"b start\\n\"\n    \"c1:\\n\"\n    \"    .word 0x12345678\\n\"\n    \"start:\\n\"\n    \"    adr r4, c1\\n\"\n    \"    ldr r5, [r4]\\n\"\n    \"    eor r0, r0, r5\\n\"\n    \"    adr r6, c2\\n\"\n    \"    ldr r7, [r6]\\n\"\n    \"    eor r1, r1, r7\\n\"\n    \"    b end\\n\"\n    \"c2:\\n\"\n    \"    .word 0x87654321\\n\"\n    \"end:\\n\";\n\nconst char *ConditionalBranching_s =\n    \"    push {r0-r3}\\n\"\n    \"    mov r12, #0\\n\"\n    \"    mov r2, #0\\n\"\n    \"    mov r1, #0\\n\"\n    \"    mov r0, sp\\n\"\n    \"loop:\\n\"\n    \"    ldrb r1, [r0], 1\\n\"\n    \"    eor r12, r12, r1\\n\"\n    \"    ror r12, r12, #12\\n\"\n    \"    add r2, r2, #1\\n\"\n    \"    cmp r2, #16\\n\"\n    \"    blt loop\\n\"\n    \"    adr r3, checksum\\n\"\n    \"    ldr r4, [r3]\\n\"\n    \"    cmp r4, r2\\n\"\n    \"    bne bad\\n\"\n    \"    mov r0, #1\\n\"\n    \"    b end\\n\"\n    \"bad:\\n\"\n    \"    mov r0, #0\\n\"\n    \"    b end\\n\"\n    \"checksum:\\n\"\n    \"    .word 0xffe8dd7f\\n\"\n    \"end:\\n\"\n    \"    add sp, sp, 16\\n\";\n\nconst char *FibonacciRecursion_s =\n    \"    adr r2, fibo\\n\"\n    \"    blx r2\\n\"\n    \"    b end\\n\"\n    \"fibo:\\n\"\n    \"    cmp r0, #2\\n\"\n    \"    movls r0, #1\\n\"\n    \"    bxls lr\\n\"\n    \"    push {r0, lr}\\n\"\n    \"    sub r0, r0, #1\\n\"\n    \"    blx r2\\n\"\n    \"    pop {r1}\\n\"\n    \"    push {r0}\\n\"\n    \"    sub r0, r1, #2\\n\"\n    \"    blx r2\\n\"\n    \"    pop {r1}\\n\"\n    \"    add r0, r0, r1\\n\"\n    \"    pop {pc}\\n\"\n    \"end:\\n\";\n\nconst char *StackTricks_s =\n    \"    adr r2, end\\n\"\n    \"    adr r3, f1\\n\"\n    \"    push {r0, r2}\\n\"\n    \"    adr r12, f0\\n\"\n    \"    blx r12\\n\"\n    \"    bx r3\\n\"\n    \"f0:\\n\"\n    \"    mov pc, lr\\n\"\n    \"f1:\\n\"\n    \"    ldr r0, [sp]\\n\"\n    \"    adr r2, f2\\n\"\n    \"    adr r3, f6\\n\"\n    \"    mov r4, #1\\n\"\n    \"    cmp r0, #2\\n\"\n    \"    movhi r3, r2\\n\"\n    \"    push {r3}\\n\"\n    \"    pop {pc}\\n\"\n    \"f2:\\n\"\n    \"    sub r0, r0, #1\\n\"\n    \"    adr r2, f4\\n\"\n    \"    adr r3, f1\\n\"\n    \"    push {r0, r2}\\n\"\n    \"    mov lr, r3\\n\"\n    \"    bx lr\\n\"\n    \"f4:\\n\"\n    \"    add r1, r1, r4\\n\"\n    \"    sub r0, r0, #1\\n\"\n    \"    adr r2, f5\\n\"\n    \"    adr r3, f1\\n\"\n    \"    push {r0, r2}\\n\"\n    \"    blx r3\\n\"\n    \"f5:\\n\"\n    \"    add r4, r4, r1\\n\"\n    \"f6:\\n\"\n    \"    pop {r0, pc}\\n\"\n    \"end:\\n\";\n\nconst char *STLDMIA_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmia r0, {r0-r12,sp,lr,pc}\\n\"\n    \"    ldmia r0, {r0-r12,sp,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmia r0, {r2-r12,sp,lr,pc}\\n\"\n    \"    ldmia r0, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmiaeq r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmiaeq r0, {r0-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmiane r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmiane r0, {r0-r12,sp,pc}\\n\";\n\nconst char *STLDMIB_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmib r0, {r0-r12,sp,lr,pc}\\n\"\n    \"    ldmib r0, {r0-r12,sp,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmib r0, {r2-r12,sp,lr,pc}\\n\"\n    \"    ldmib r0, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmibeq r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmibeq r0, {r0-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmibne r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmibne r0, {r0-r12,sp,pc}\\n\";\n\nconst char *STLDMDA_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmda r0, {r0-r12,sp,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmda r0, {r0-r12,sp,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmda r0, {r2-r12,sp,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmda r0, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdaeq r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmdaeq r0, {r0-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdane r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmdane r0, {r0-r12,sp,pc}\\n\";\n\nconst char *STLDMDB_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmdb r0, {r0-r12,sp,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmdb r0, {r0-r12,sp,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmdb r0, {r2-r12,sp,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmdb r0, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdbeq r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmdbeq r0, {r0-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdbne r0, {r0-r12,sp,pc}\\n\"\n    \"    ldmdbne r0, {r0-r12,sp,pc}\\n\";\n\nconst char *STMDB_LDMIA_post_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmdb r0!, {r0-r12,sp,lr,pc}\\n\"\n    \"    add r0, r0, #4\\n\"\n    \"    ldmia r0!, {r1-r12,sp,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmdb r0!, {r2-r12,sp,lr,pc}\\n\"\n    \"    ldmia r0!, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdbeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmiaeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdbne r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmiane r0!, {r2-r12,sp,pc}\\n\";\n\nconst char *STMDA_LDMIB_post_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmda r0!, {r0-r12,sp,lr,pc}\\n\"\n    \"    add r0, r0, #4\\n\"\n    \"    ldmib r0!, {r1-r12,sp,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmda r0!, {r2-r12,sp,lr,pc}\\n\"\n    \"    ldmib r0!, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdaeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmibeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmdane r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmibne r0!, {r2-r12,sp,pc}\\n\";\n\nconst char *STMIB_LDMDA_post_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmib r0!, {r0-r12,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmda r0!, {r1-r12,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmib r0!, {r2-r12,sp,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmda r0!, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmibeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmdaeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmibne r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmdane r0!, {r2-r12,sp,pc}\\n\";\n\nconst char *STMIA_LDMDB_post_s =\n    \"    sub r0, sp, #128\\n\"\n    \"    adr lr, pos1\\n\"\n    \"    stmia r0!, {r0-r12,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmdb r0!, {r1-r12,pc}\\n\"\n    \"pos1:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    adr lr, pos2\\n\"\n    \"    stmia r0!, {r2-r12,sp,lr,pc}\\n\"\n    \"    sub r0, r0, #4\\n\"\n    \"    ldmdb r0!, {r2-r12,sp,pc}\\n\"\n    \"pos2:\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmiaeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmdbeq r0!, {r2-r12,sp,pc}\\n\"\n    \"    sub r0, r0, #128\\n\"\n    \"    stmiane r0!, {r2-r12,sp,pc}\\n\"\n    \"    ldmdbne r0!, {r2-r12,sp,pc}\\n\";\n\nconst char *LDREXTest_s =\n    \"   mov r12, sp\\n\"\n    \"   mov r2, #0\\n\"\n    \"loopmemset:\\n\"\n    \"   strh r2, [r11, r2]\\n\"\n    \"   add r2, #2\\n\"\n    \"   cmp r2, 4096\\n\"\n    \"   bne loopmemset\\n\"\n    // align 4 r11 (to support ldrexd)\n    \"   orr r11, r11, 0xf\\n\"\n    \"   add r11, r11, 1\\n\"\n    // test 1\n    \"   ldrex r0, [r11]\\n\"\n    \"   mov r1, #0xff\\n\"\n    \"   strex r2, r1, [r11]\\n\"\n    \"   ldr r1, [r11]\\n\"\n    \"   push {r0, r1, r2}\\n\"\n    // test 2\n    \"   add r10, r11, #256\\n\"\n    \"   ldrex r0, [r10]\\n\"\n    \"   mov r1, #0xfa7\\n\"\n    \"   add r10, r11, #256\\n\"\n    \"   strex r2, r1, [r10]\\n\"\n    \"   ldr r1, [r11, #256]\\n\"\n    \"   push {r0, r1, r2}\\n\"\n    // test 3\n    \"   ldrex r0, [r11]\\n\"\n    \"   mov r1, #0xfa8\\n\"\n    \"   add r10, r11, #300\\n\"\n    \"   strex r2, r1, [r10]\\n\"\n    \"   ldr r1, [r10]\\n\"\n    \"   push {r0, r1, r2}\\n\"\n    // test 4\n    \"   add r10, r11, #2048\\n\"\n    \"   ldrex r0, [r11]\\n\"\n    \"   ldrex r1, [r10]\\n\"\n    \"   mov r2, #0xfa8\\n\"\n    \"   mov r3, #0xc58\\n\"\n    \"   strex r4, r2, [r11]\\n\"\n    \"   strex r5, r3, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 5\n    \"   mov r10, #3124\\n\"\n    \"   add r10, r11, r10\\n\"\n    \"   ldrex r0, [r11]\\n\"\n    \"   ldrex r1, [r10]\\n\"\n    \"   mov r2, #0x1a4\\n\"\n    \"   mov r3, #0x453\\n\"\n    \"   strex r4, r2, [r10]\\n\"\n    \"   strex r5, r3, [r11]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 6\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   mov r10, #3096\\n\"\n    \"   add r10, r11, r10\\n\"\n    \"   cmp r8, r9\\n\"\n    \"   ldrexle r0, [r11]\\n\"\n    \"   ldrexgt r1, [r10]\\n\"\n    \"   mov r2, #0x58\\n\"\n    \"   mov r3, #0x761\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   strexle r5, r3, [r11]\\n\"\n    \"   strexgt r4, r2, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 7\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   mov r10, #3080\\n\"\n    \"   add r10, r11, r10\\n\"\n    \"   cmp r8, r9\\n\"\n    \"   ldrexle r0, [r11]\\n\"\n    \"   ldrexgt r1, [r10]\\n\"\n    \"   mov r2, #0x146\\n\"\n    \"   mov r3, #0x9de\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   strexle r4, r2, [r10]\\n\"\n    \"   strexgt r5, r3, [r11]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 8\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   mov r10, #3000\\n\"\n    \"   add r10, r11, r10\\n\"\n    \"   cmp r9, r8\\n\"\n    \"   ldrexhle r0, [r11]\\n\"\n    \"   ldrexbgt r1, [r10]\\n\"\n    \"   mov r2, #0xb5\\n\"\n    \"   mov r3, #0xea\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   strexhle r5, r3, [r11]\\n\"\n    \"   strexbgt r4, r2, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 9\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   mov r10, #1032\\n\"\n    \"   add r10, r11, r10\\n\"\n    \"   cmp r9, r8\\n\"\n    \"   ldrexdle r8, r9, [r11]\\n\"\n    \"   ldrexbgt r1, [r10]\\n\"\n    \"   mov r2, #0x78\\n\"\n    \"   mov r3, #0x46d\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   strexble r5, r3, [r11]\\n\"\n    \"   strexdgt r4, r8, r9, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r9}\\n\"\n\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n"
  },
  {
    "path": "test/Patch/ARM/ComparedExecutor_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef COMPAREDEXECUTOR_ARM_H\n#define COMPAREDEXECUTOR_ARM_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n#include \"TestSetup/ShellcodeTester.h\"\n\n#define CPU_CPU \"cortex-a57\"\n#define CPU_MATTRS \"neon\"\n\nextern const char *GPRSave_s;\nextern const char *GPRShuffle_s;\nextern const char *RelativeAddressing_s;\nextern const char *ConditionalBranching_s;\nextern const char *FibonacciRecursion_s;\nextern const char *StackTricks_s;\nextern const char *STLDMIA_s;\nextern const char *STLDMIB_s;\nextern const char *STLDMDA_s;\nextern const char *STLDMDB_s;\nextern const char *STMDB_LDMIA_post_s;\nextern const char *STMDA_LDMIB_post_s;\nextern const char *STMIB_LDMDA_post_s;\nextern const char *STMIA_LDMDB_post_s;\nextern const char *LDREXTest_s;\n\nclass ComparedExecutor_ARM : public ShellcodeTester {\n\npublic:\n  ComparedExecutor_ARM() : ShellcodeTester(CPU_CPU, {CPU_MATTRS}) {}\n\n  QBDI::Context jitExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                        llvm::sys::MemoryBlock &stack);\n\n  QBDI::Context realExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                         llvm::sys::MemoryBlock &stack);\n\n  InMemoryObject compileWithContextSwitch(const char *source);\n\n  void initContext(QBDI::Context &ctx);\n};\n\n#endif\n"
  },
  {
    "path": "test/Patch/ARM/ComparedExecutor_Thumb.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"ComparedExecutor_Thumb.h\"\n#include \"QBDI/Config.h\"\n#include \"QBDI/Memory.hpp\"\n#include \"Patch/Utils.h\"\n\nconst char CPU[] = CPU_CPU;\nconst std::vector<std::string> MATTRS = {CPU_MATTRS, \"thumb2\", \"v7\"};\n\nInMemoryObject\nComparedExecutor_Thumb::compileWithContextSwitch(const char *source) {\n  std::ostringstream finalSource;\n\n  finalSource << \".thumb\\n\"\n              << \"push.w {lr}\\n\"\n              << \"add.w r0, r1, #\"\n              << offsetof(QBDI::Context, fprState.vreg.d[0]) << \"\\n\"\n              << \"vldmia\tr0!, {d0-d15}\\n\"\n#if QBDI_NUM_FPR == 32\n              << \"vldmia\tr0, {d16-d31}\\n\"\n#endif\n              << \"ldr.w r0, [r1, #\" << offsetof(QBDI::Context, gprState.cpsr)\n              << \"]\\n\"\n              << \"msr APSR_nzcvqg, r0\\n\"\n              // backup SP\n              << \"str.w sp, [r1, #\" << offsetof(QBDI::Context, hostState.sp)\n              << \"]\\n\"\n              // set SP LR\n              << \"add.w r0, r1, #\" << offsetof(QBDI::Context, gprState.sp)\n              << \"\\n\"\n              << \"ldm.w r0, {r2,lr}\\n\"\n              << \"mov.w\tsp, r2\\n\"\n              // set backup\n              << \"push.w {r0-r1}\\n\"\n              // set r0-r12 SP LR\n              << \"add.w r0, r1, #\" << offsetof(QBDI::Context, gprState.r0)\n              << \"\\n\"\n              << \"ldm r0, {r0-r12}\\n\"\n              //<< \"bkpt #0\\n\"\n              << \".align 2\\n\"\n              << source << \"str.w r0, [sp]\\n\"\n              << \"ldr.w r0, [sp, #4]\\n\"\n              // backup r1-r12 SP LR\n              << \"add.w r0, r0, #\" << offsetof(QBDI::Context, gprState.r1)\n              << \"\\n\"\n              << \"stm r0!, {r1-r12}\\n\"\n              << \"mov r2, sp\\n\"\n              << \"stm r0, {r2,lr}\\n\"\n              << \"pop.w {r0-r1}\\n\"\n              // backup r0\n              << \"str.w r0, [r1, #\" << offsetof(QBDI::Context, gprState.r0)\n              << \"]\\n\"\n              // restore sp\n              << \"ldr.w sp, [r1, #\" << offsetof(QBDI::Context, hostState.sp)\n              << \"]\\n\"\n              << \"mrs r0, APSR\\n\"\n              << \"str.w r0, [r1, #\" << offsetof(QBDI::Context, gprState.cpsr)\n              << \"]\\n\"\n              << \"add.w r0, r1, #\"\n              << offsetof(QBDI::Context, fprState.vreg.d[0]) << \"\\n\"\n              << \"vstmia  r0!, {d0-d15}\\n\"\n#if QBDI_NUM_FPR == 32\n              << \"vstmia  r0, {d16-d31}\\n\"\n#endif\n              << \"pop.w {lr}\\n\"\n              << \"bx lr\\n\";\n\n  // printf(\"%s\\n\",finalSource.str().c_str());\n\n  return InMemoryObject(finalSource.str().c_str(), CPU, \"thumb\", MATTRS);\n}\n\nQBDI::Context ComparedExecutor_Thumb::jitExec(llvm::ArrayRef<uint8_t> code,\n                                              QBDI::Context &inputState,\n                                              llvm::sys::MemoryBlock &stack) {\n  QBDI::Context outputState;\n  QBDI::Context outerState;\n  llvm::sys::MemoryBlock ctxBlock;\n  llvm::sys::MemoryBlock outerStack;\n  std::error_code ec;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  outerStack = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  memset((void *)&outerState, 0, sizeof(QBDI::Context));\n  // Put the inputState on the stack\n  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  memcpy((void *)ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Prepare the outerState to fake the realExec() action\n  outerState.gprState.sp =\n      (QBDI::rword)outerStack.base() + outerStack.allocatedSize();\n  outerState.gprState.lr = (QBDI::rword)0;\n  outerState.gprState.r1 = (QBDI::rword)ctxBlock.base();\n\n  vm.setGPRState(&outerState.gprState);\n  vm.setFPRState(&outerState.fprState);\n  vm.addInstrumentedRange((QBDI::rword)code.data(),\n                          (QBDI::rword)code.data() + code.size());\n  vm.run(((QBDI::rword)code.data()) | 1, 0);\n  vm.removeInstrumentedRange((QBDI::rword)code.data(),\n                             (QBDI::rword)code.data() + code.size());\n\n  memcpy((void *)&outputState, (void *)ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n  llvm::sys::Memory::releaseMappedMemory(outerStack);\n\n  return outputState;\n}\n\nQBDI::Context ComparedExecutor_Thumb::realExec(llvm::ArrayRef<uint8_t> code,\n                                               QBDI::Context &inputState,\n                                               llvm::sys::MemoryBlock &stack) {\n\n  QBDI::Context outputState;\n  std::error_code ec;\n  llvm::sys::MemoryBlock ctxBlock;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n\n  // Put the inputState on the stack\n  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  // Assemble the sources\n  // Copy the input context\n  memcpy(ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Execute\n  {\n    register uint32_t ctxBlockBase asm(\"r1\") = (uint32_t)ctxBlock.base();\n    register uint32_t codeData asm(\"r2\") = (((uint32_t)code.data()) | 1);\n    asm volatile inline(\n        \"push {r0, lr}\\n\"\n        \"blx r2\\n\"\n        \"pop {r0, lr}\\n\"\n        :\n        : \"r\"(codeData), \"r\"(ctxBlockBase)\n        : \"r0\", \"r3\", \"r4\", \"r5\", \"r6\", \"r7\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\",\n          \"d0\", \"d1\", \"d2\", \"d3\", \"d4\", \"d5\", \"d6\", \"d7\", \"d8\", \"d9\", \"d10\",\n          \"d11\", \"d12\", \"d13\", \"d14\", \"d15\", \"d16\", \"d17\", \"d18\", \"d19\", \"d20\",\n          \"d21\", \"d22\", \"d23\", \"d24\", \"d25\", \"d26\", \"d27\", \"d28\", \"d29\", \"d30\",\n          \"d31\", \"memory\");\n  }\n  // Get the output context\n  memcpy((void *)&outputState, ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n\n  return outputState;\n}\n\nvoid ComparedExecutor_Thumb::initContext(QBDI::Context &ctx) {\n  memset(&ctx, 0, sizeof(QBDI::Context));\n  ctx.gprState.r0 = get_random();\n  ctx.gprState.r1 = get_random();\n  ctx.gprState.r2 = get_random();\n  ctx.gprState.r3 = get_random();\n  ctx.gprState.r4 = get_random();\n  ctx.gprState.r5 = get_random();\n  ctx.gprState.r6 = get_random();\n  ctx.gprState.r7 = get_random();\n  ctx.gprState.r8 = get_random();\n  ctx.gprState.r9 = get_random();\n  ctx.gprState.r10 = get_random();\n  ctx.gprState.r11 = get_random();\n  ctx.gprState.r12 = get_random();\n  ctx.gprState.lr = get_random();\n}\n\nconst char *tGPRSave_s =\n    \"    mov r0, #1\\n\"\n    \"    mov r1, #2\\n\"\n    \"    mov r2, #3\\n\"\n    \"    mov r3, #4\\n\"\n    \"    mov r4, #5\\n\"\n    \"    mov r5, #6\\n\"\n    \"    mov r6, #7\\n\"\n    \"    mov r7, #8\\n\"\n    \"    mov r8, #9\\n\"\n    \"    mov r9, #10\\n\"\n    \"    mov r10, #11\\n\"\n    \"    mov r11, #12\\n\"\n    \"    mov r12, #13\\n\"\n    \"    mov lr, #14\\n\";\n\nconst char *tGPRShuffle_s =\n    \"    push {r0-r12,lr}\\n\"\n    \"    pop {r3}\\n\"\n    \"    pop {r11}\\n\"\n    \"    pop {r7}\\n\"\n    \"    pop {r2}\\n\"\n    \"    pop {r12}\\n\"\n    \"    pop {r10}\\n\"\n    \"    pop {lr}\\n\"\n    \"    pop {r1}\\n\"\n    \"    pop {r5}\\n\"\n    \"    pop {r6}\\n\"\n    \"    pop {r0}\\n\"\n    \"    pop {r4}\\n\"\n    \"    pop {r9}\\n\"\n    \"    pop {r8}\\n\";\n\nconst char *tRelativeAddressing_s =\n    \"    b start\\n\"\n    \".align 2\\n\"\n    \"c1:\\n\"\n    \"    .word 0x12345678\\n\"\n    \"start:\\n\"\n    \"    adr.w r0, c1\\n\"\n    \"    ldr r1, [r0]\\n\"\n    \"    eor.w r12, r12, r1\\n\"\n    \"    ldr r2, c2\\n\"\n    \"    eor.w r11, r11, r2\\n\"\n    \"    b.w t2\\n\"\n    \"c2:\\n\"\n    \"    .word 0x76543210\\n\"\n    \".align 2\\n\"\n    \"t2:\\n\"\n    // test with 4bytes unaligned instruction\n    \"    nop\\n\"\n    \"    adr r3, #12\\n\"\n    \"    ldr r4, [r3]\\n\"\n    \"    eor.w r10, r10, r4\\n\"\n    \"    ldr.w r5, c3\\n\"\n    \"    eor.w r9, r9, r5\\n\"\n    \"    b.w end\\n\"\n    \"    .word 0x76543210\\n\"\n    \"c3:\\n\"\n    \"    .word 0x89abcdef\\n\"\n    \"end:\\n\";\n\nconst char *tFibonacciRecursion_s =\n    \"    adr r2, fibo\\n\"\n    \"    eor r2, r2, #1\\n\"\n    \"    blx r2\\n\"\n    \"    b end\\n\"\n    \".align 2\\n\"\n    \"fibo:\\n\"\n    \"    cmp r0, #2\\n\"\n    \"    itt ls\\n\"\n    \"    movls r0, #1\\n\"\n    \"    bxls lr\\n\"\n    \"    push {r0, lr}\\n\"\n    \"    sub r0, r0, #1\\n\"\n    \"    blx r2\\n\"\n    \"    pop {r1}\\n\"\n    \"    push {r0}\\n\"\n    \"    sub r0, r1, #2\\n\"\n    \"    blx r2\\n\"\n    \"    pop {r1}\\n\"\n    \"    add r0, r0, r1\\n\"\n    \"    pop {pc}\\n\"\n    \"end:\\n\";\n\nconst char *tBranchCondTest_s =\n    \"    cmp r0, r1\\n\"\n    \"    ble t1\\n\"\n    \"    add r0, #1\\n\"\n    \"    b t2\\n\"\n    \"t1:\\n\"\n    \"    add r1, #1\\n\"\n    \"t2:\\n\"\n    \"    cmp r2, r3\\n\"\n    \"    it le\\n\"\n    \"    ble t3\\n\"\n    \"    add r2, #1\\n\"\n    \"    b t4\\n\"\n    \"t3:\\n\"\n    \"    add r3, #1\\n\"\n    \"t4:\\n\"\n    \"    cmp r4, r5\\n\"\n    \"    ble.w t5\\n\"\n    \"    add r4, #1\\n\"\n    \"    b.w t6\\n\"\n    \"t5:\\n\"\n    \"    add r5, #1\\n\"\n    \"t6:\\n\"\n    \"    cmp r6, r7\\n\"\n    \"    it le\\n\"\n    \"    ble.w t7\\n\"\n    \"    add r6, #1\\n\"\n    \"    b.w t8\\n\"\n    \"t7:\\n\"\n    \"    add r8, #1\\n\"\n    \"t8:\\n\"\n    \"    mov r7, 1\\n\"\n    \"    cbnz r7, t9\\n\"\n    \"    mov.w r12, #37\\n\"\n    \"t9:\\n\"\n    \"    cbz r7, t10\\n\"\n    \"    mov.w r11, #42\\n\"\n    \"t10:\\n\"\n    \"    cbz r7, t11\\n\"\n    \"    mov.w r10, #42\\n\"\n    \"t11:\\n\"\n    \"    cbnz r7, t12\\n\"\n    \"    mov.w r9, #37\\n\"\n    \".align 2\\n\"\n    \"t12:\\n\"\n    \"   ldr pc, [pc, #4]\\n\"\n    \"   bkpt #9\\n\"\n    \"   nop\\n\"\n    \"   .long t13()\\n\"\n    \".thumb\\n\"\n    \".thumb_func\\n\"\n    \"t13:\\n\";\n\nconst char *tBranchLinkCondTest_s =\n    \"    nop\\n\"\n    \"    cmp r0, r1\\n\"\n    \"    it le\\n\"\n    \"    blle t1\\n\"\n    \"    it gt\\n\"\n    \"    blgt t2\\n\"\n    \"    b t3\\n\"\n    \"t1:\\n\"\n    \"    add r3, #1\\n\"\n    \"    mov r2, lr\\n\"\n    \"    bx lr\\n\"\n    \"t2:\\n\"\n    \"    add r3, #2\\n\"\n    \"    mov r2, lr\\n\"\n    \"    bx lr\\n\"\n    \"t3:\\n\"\n    \"    it le\\n\"\n    \"    blxle armTargetR12\\n\"\n    \"    it gt\\n\"\n    \"    blxgt armTargetR12\\n\"\n    \"    b t6\\n\"\n    \".align 2\\n\"\n    \".code 32\\n\"\n    \"armTargetR12:\\n\"\n    \"    eor r12, r12, lr\\n\"\n    \"    bx lr\\n\"\n    \".thumb\\n\"\n    \"t6:\\n\";\n\nconst char *tBranchRegisterCondTest_s =\n    \"   mov r9, #0\\n\"\n    \"   mov r10, #0x60\\n\"\n    \"   mov r11, #0x1fff\\n\"\n    \"   mov r12, #0x1553\\n\"\n    \"   b.w beginTest\\n\"\n    \".align 2\\n\"\n    \".thumb_func\\n\"\n    \"thumbTargetR11:\\n\"\n    \"   adr r3, #-4\\n\"\n    \"   eor r3, #1\\n\"\n    \"   sub r2, lr, #8\\n\"\n    \"   eor r11, r11, r0\\n\"\n    \"   push {lr}\\n\"\n    \"   pop {pc}\\n\"\n    \".align 2\\n\"\n    \".code 32\\n\"\n    \"armTargetR12:\\n\"\n    \"   push {lr}\\n\"\n    \"   blx thumbTargetR11\\n\"\n    \"   eor r12, r12, r0\\n\"\n    \"   pop {pc}\\n\"\n    \".align 2\\n\"\n    \".code 32\\n\"\n    \"armTargetR10:\\n\"\n    \"   push {lr}\\n\"\n    \"   blx r6\\n\"\n    \"   eor r10, r10, r0\\n\"\n    \"   pop {pc}\\n\"\n    \"addrTable:\\n\"\n    \"   .long thumbTargetR11()\\n\"\n    \"   .long armTargetR12()\\n\"\n    \".align 2\\n\"\n    \".thumb\\n\"\n    \"beginTest:\\n\"\n    \"   mov r0, #1\\n\"\n    \"   blx armTargetR12\\n\"\n    \"   mov r0, #2\\n\"\n    \"   blx r2\\n\"\n    \"   mov r0, #4\\n\"\n    \"   blx r3\\n\"\n    \"   adr r4, addrTable\\n\"\n    \"   mov r0, #8\\n\"\n    \"   ldr r5, [r4]\\n\"\n    \"   blx r5\\n\"\n    \"   mov r0, #16\\n\"\n    \"   ldr r5, [r4, #4]\\n\"\n    \"   blx r5\\n\"\n    \"   mov r0, #32\\n\"\n    \"   ldr r6, [r4]\\n\"\n    \"   blx armTargetR10\\n\"\n    \"   mov r0, #64\\n\"\n    \"   ldr r6, [r4, #4]\\n\"\n    \"   blx armTargetR10\\n\"\n    \"   mov r0, #128\\n\"\n    \"   ldr lr, [r4]\\n\"\n    \"   blx lr\\n\"\n    \"   mov r0, #256\\n\"\n    \"   ldr lr, [r4, #4]\\n\"\n    \"   blx lr\\n\"\n    \"   mov r0, #512\\n\"\n    \"   ldr r5, [r4]\\n\"\n    \"   adr lr, t1\\n\"\n    \"   eor lr, #1\\n\"\n    \"   bx r5\\n\"\n    \".align 2\\n\"\n    \"t1:\\n\"\n    \"   mov r0, #1024\\n\"\n    \"   ldr r5, [r4, #4]\\n\"\n    \"   adr lr, t2\\n\"\n    \"   eor lr, #1\\n\"\n    \"   bx r5\\n\"\n    \".align 2\\n\"\n    \"t2:\\n\"\n    \"   mov r0, #2048\\n\"\n    \"   ldr r5, [r4]\\n\"\n    \"   adr lr, t3\\n\"\n    \"   eors lr, #1\\n\"\n    \"   it ge\\n\"\n    \"   bxge r5\\n\"\n    \"   eor r9, #1\\n\"\n    \"   it lt\\n\"\n    \"   bxlt r5\\n\"\n    \".align 2\\n\"\n    \"t3:\\n\"\n    \"   mov r0, #4096\\n\"\n    \"   ldr r5, [r4, #4]\\n\"\n    \"   adr lr, t4\\n\"\n    \"   eors lr, #1\\n\"\n    \"   it ge\\n\"\n    \"   bxge r5\\n\"\n    \"   eor r9, #2\\n\"\n    \"   it lt\\n\"\n    \"   bxlt r5\\n\"\n    \".align 2\\n\"\n    \"t4:\\n\"\n    \"   bx pc\\n\"\n    \"   bkpt #2\\n\"\n    \".code 32\\n\"\n    \"   eors r9, r9, #4\\n\"\n    \"   blx t5\\n\"\n    \"   bkpt #3\\n\"\n    \".thumb\\n\"\n    \".thumb_func\\n\"\n    \"t5:\\n\"\n    \"   nop\\n\"\n    \"   it le\\n\"\n    \"   bxle pc\\n\"\n    \"   b t6\\n\"\n    \".code 32\\n\"\n    \"   eors r9, r9, #8\\n\"\n    \"   blx t7\\n\"\n    \"   bkpt #5\\n\"\n    \".thumb\\n\"\n    \".thumb_func\\n\"\n    \"t6:\\n\"\n    \"   nop\\n\"\n    \"   it gt\\n\"\n    \"   bxgt pc\\n\"\n    \"   bkpt #6\\n\"\n    \".code 32\\n\"\n    \"   eors r9, r9, #16\\n\"\n    \"   blx t7\\n\"\n    \"   bkpt #7\\n\"\n    \".align 2\\n\"\n    \".thumb\\n\"\n    \".thumb_func\\n\"\n    \"t7:\\n\";\n\nconst char *tPushPopTest_s =\n    // part 1 : tLDMIA, tSTMIA\n    \"p1:\\n\"\n    \"   sub sp, sp, #28\\n\"\n    \"   mov r0, sp\\n\"\n    \"   stm r0!, {r1-r7}\\n\"\n    \"   b p2\\n\"\n    \"p1_end:\\n\"\n    \"   mov r0, sp\\n\"\n    \"   ldm r0!, {r1-r7}\\n\"\n    \"   mov sp, r0\\n\"\n    \"   b end\\n\"\n    // part 2 : tLDMIA, tSTMIA\n    \"p2:\\n\"\n    \"   sub sp, sp, #32\\n\"\n    \"   cmp r1, r2\\n\"\n    \"   ittee lt\\n\"\n    \"   movlt r5, sp\\n\"\n    \"   stmlt r5!, {r0-r4,r6,r7}\\n\"\n    \"   movge r0, sp\\n\"\n    \"   stmge r0!, {r1-r7}\\n\"\n    \"   b p3\\n\"\n    \".align 2\\n\"\n    \"p2_end:\\n\"\n    \"   cmp r1, r2\\n\"\n    \"   ittee lt\\n\"\n    \"   movlt r5, sp\\n\"\n    \"   ldmlt r5!, {r0-r4,r6,r7}\\n\"\n    \"   movge r0, sp\\n\"\n    \"   ldmge r0!, {r1-r7}\\n\"\n    \"   add sp, sp, #28\\n\"\n    \"   str r5, [sp], #4\\n\"\n    \"   b p1_end\\n\"\n    // part 3 : tPUSH, tPOP\n    \"p3:\\n\"\n    \"   adr lr, p2_end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   push {r0-r7, lr}\\n\"\n    \"   b p4\\n\"\n    \".align 2\\n\"\n    \"p3_end:\\n\"\n    \"   pop {r0-r7, pc}\\n\"\n    // part 4 : tPUSH, tPOP\n    \"p4:\\n\"\n    \"   adr lr, p3_end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   cmp r1, r2\\n\"\n    \"   ite lt\\n\"\n    \"   pushlt {r1-r7, lr}\\n\"\n    \"   pushge {r0-r6, lr}\\n\"\n    \"   b p5\\n\"\n    \"p4_end:\\n\"\n    \"   cmp r3, r4\\n\"\n    \"   ite lt\\n\"\n    \"   poplt {r0-r7}\\n\"\n    \"   popge {r0,r2-r7, pc}\\n\"\n    \"   str r7, [sp, #-8]\\n\"\n    \"   bx r7\\n\"\n    // part 5: tLDMIA, tSTMIA no wback\n    \"p5:\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   sub r0, sp, #32\\n\"\n    \"   ite le\\n\"\n    \"   stmle r0, {r0-r7}\\n\"\n    \"   stmgt r0, {r0,r3-r5}\\n\"\n    \"   cmp r9, r10\\n\"\n    \"   sub r2, r0, #32\\n\"\n    \"   ite le\\n\"\n    \"   stmle r2, {r0-r7}\\n\"\n    \"   stmgt r2, {r0-r2,r5,r7}\\n\"\n    \"p5_end:\\n\"\n    \"   sub r7, sp, #64\\n\"\n    \"   cmp r8, r12\\n\"\n    \"   ite le\\n\"\n    \"   ldmle r7, {r1,r3,r7}\\n\"\n    \"   ldmgt r7, {r1,r4-r7}\\n\"\n    \"   cmp r8, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldmle r1, {r0,r4}\\n\"\n    \"   ldmgt r1, {r0-r5}\\n\"\n    \"   str r7, [sp, #-8]\\n\"\n    \"   str r4, [sp, #-12]\\n\"\n    \"   b p4_end\\n\"\n    \"end:\\n\";\n\nconst char *tLdmiaStmdbWbackTest_s =\n    // part 1 : PUSH / POP\n    \"p1:\\n\"\n    \"   adr lr, end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   push.w {r0-r12, lr}\\n\"\n    \"   b p2\\n\"\n    \"p1_end:\\n\"\n    \"   pop.w {r0-r12, pc}\\n\"\n    // part 2 : PUSH / POP\n    \"p2:\\n\"\n    \"   sub sp, sp, #4\\n\"\n    \"   mov lr, sp\\n\"\n    \"   push.w {r0-r12, lr}\\n\"\n    \"   b p3\\n\"\n    \".align 2\\n\"\n    \"p2_end:\\n\"\n    \"   pop.w {r0-r12, lr}\\n\"\n    \"   str lr, [sp], #4\\n\"\n    \"   b p1_end\\n\"\n    // part 3 : PUSH / POP\n    \"   b p3\\n\"\n    \"p3:\\n\"\n    \"   adr lr, p2_end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   itee gt\\n\"\n    \"   pushgt.w {r0-r12, lr}\\n\"\n    \"   pushle.w {r0-r8,r10-r12, lr}\\n\"\n    \"   pushle.w {r9}\\n\"\n    \"   sub sp, sp, #4\\n\"\n    \"   b p4\\n\"\n    \"p3_end:\\n\"\n    \"   add sp, sp, #4\\n\"\n    \"   cmp r9, r12\\n\"\n    \"   ite gt\\n\"\n    \"   popgt.w {r0-r12, lr}\\n\"\n    \"   pople.w {r0-r12, pc}\\n\"\n    \"   str lr, [sp, #-56]\\n\"\n    \"   bx lr\\n\"\n    // part 4 : LDMIA STMDB\n    \"p4:\\n\"\n    \"   adr lr, p4_ending\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub r9, sp, #4\\n\"\n    \"   stmdb r9!, {r0-r8,r10-r12, lr}\\n\"\n    \"   mov sp, r9\\n\"\n    \"   b p5\\n\"\n    \"p4_end:\\n\"\n    \"   mov r2, sp\\n\"\n    \"   ldmia r2!, {r0,r1,r3-r12, pc}\\n\"\n    \"   bkpt #10\\n\"\n    \".align 2\\n\"\n    \"p4_ending:\\n\"\n    \"   mov sp, r2\\n\"\n    \"   str r9, [sp], #4\\n\"\n    \"   b p3_end\\n\"\n    // part 5 : LDMIA STMDB\n    \"p5:\\n\"\n    \"   sub r2, sp, #8\\n\"\n    \"   str r2, [r2]\\n\"\n    \"   stmdb r2!, {r0,r3-r12}\\n\"\n    \"   mov sp, r2\\n\"\n    \"   b p6\\n\"\n    \".align 2\\n\"\n    \"p5_end:\\n\"\n    \"   mov r3, sp\\n\"\n    \"   ldmia r3!, {r0-r1,r4-r12, lr}\\n\"\n    \"   mov sp, r3\\n\"\n    \"   str r9, [sp], #4\\n\"\n    \"   b p4_end\\n\"\n    // part 6 : LDMIA STMDB\n    \"p6:\\n\"\n    \"   adr lr, p5_end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub r2, sp, #4\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   ite gt\\n\"\n    \"   stmdbgt r2!, {r0,r3-r12, lr}\\n\"\n    \"   stmdble r2!, {r0-r1,r3-r11, lr}\\n\"\n    \"   mov sp, r2\\n\"\n    \"p6_end:\\n\"\n    \"   mov r5, sp\\n\"\n    \"   cmp r10, r12\\n\"\n    \"   itet gt\\n\"\n    \"   addgt sp, sp, #52\\n\"\n    \"   ldmiale r5!, {r0-r4,r6-r12}\\n\"\n    \"   ldmiagt r5!, {r0-r3,r6-r12, pc}\\n\"\n    \"   mov sp, r5\\n\"\n    \"   str r12, [sp], #4\\n\"\n    \"   blx r12\\n\"\n    \".align 2\\n\"\n    \"end:\\n\";\n\nconst char *tLdmdbStmiaWbackTest_s =\n    // part 1 : LDMDB STMIA\n    \"p1:\\n\"\n    \"   adr lr, p1_ending\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub sp, sp, #56\\n\"\n    \"   mov r9, sp\\n\"\n    \"   stmia r9!, {r0-r8,r10-r12, lr}\\n\"\n    \"   b p2\\n\"\n    \"p1_end:\\n\"\n    \"   add sp, sp, #52\\n\"\n    \"   mov r2, sp\\n\"\n    \"   ldmdb r2!, {r0-r1,r3-r12, pc}\\n\"\n    \"   bkpt #10\\n\"\n    \".align 2\\n\"\n    \"p1_ending:\\n\"\n    \"   str r2, [sp], #4\\n\"\n    \"   b end\\n\"\n    // part 2 : LDMDB STMIA\n    \"p2:\\n\"\n    \"   sub sp, sp, #56\\n\"\n    \"   mov r2, sp\\n\"\n    \"   str r2, [sp, #44]\\n\"\n    \"   stmia r2!, {r0,r3-r12}\\n\"\n    \"   b p3\\n\"\n    \".align 2\\n\"\n    \"p2_end:\\n\"\n    \"   add sp, sp, #48\\n\"\n    \"   mov r3, sp\\n\"\n    \"   ldmdb r3!, {r0,r1,r4-r12, lr}\\n\"\n    \"   str r3, [sp], #4\\n\"\n    \"   str r1, [sp], #4\\n\"\n    \"   b p1_end\\n\"\n    // part 3 : LDMDB STMIA\n    \"p3:\\n\"\n    \"   adr lr, p2_end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub sp, sp, #52\\n\"\n    \"   mov r2, sp\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   ite gt\\n\"\n    \"   stmiagt r2!, {r0,r3-r12, lr}\\n\"\n    \"   stmiale r2!, {r0-r1,r3-r11, lr}\\n\"\n    \"   str r2, [sp, #-4]\\n\"\n    \"p3_end:\\n\"\n    \"   add r5, sp, #48\\n\"\n    \"   cmp r10, r12\\n\"\n    \"   itet gt\\n\"\n    \"   addgt sp, sp, #52\\n\"\n    \"   ldmdble r5!, {r0-r4,r6-r12}\\n\"\n    \"   ldmdbgt r5!, {r0-r3,r6-r12, pc}\\n\"\n    \"   add r5, r5, #48\\n\"\n    \"   mov sp, r5\\n\"\n    \"   str r12, [sp], #4\\n\"\n    \"   blx r12\\n\"\n    \".align 2\\n\"\n    \"end:\\n\";\n\nconst char *tLdmiaStmdbTest_s =\n    // part 1 : LDMIA STMDB\n    \"p1:\\n\"\n    \"   adr lr, p1_ending\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub r9, sp, #4\\n\"\n    \"   stmdb r9, {r0-r12, lr}\\n\"\n    \"   sub sp, sp, #60\\n\"\n    \"   b p2\\n\"\n    \"p1_end:\\n\"\n    \"   mov r2, sp\\n\"\n    \"   ldmia r2, {r0-r12, pc}\\n\"\n    \"   bkpt #10\\n\"\n    \".align 2\\n\"\n    \"p1_ending:\\n\"\n    \"   add sp, sp, #56\\n\"\n    \"   str r9, [sp], #4\\n\"\n    \"   b end\\n\"\n    // part 2 : LDMIA STMDB\n    \"p2:\\n\"\n    \"   sub r2, sp, #8\\n\"\n    \"   str r2, [sp, #-8]\\n\"\n    \"   stmdb r2, {r0,r2-r12}\\n\"\n    \"   sub sp, sp, #60\\n\"\n    \"   b p3\\n\"\n    \".align 2\\n\"\n    \"p2_end:\\n\"\n    \"   mov r3, sp\\n\"\n    \"   ldmia r3, {r0-r1,r3-r12, lr}\\n\"\n    \"   str r3, [sp, #56]!\\n\"\n    \"   add sp, sp, #4\\n\"\n    \"   b p1_end\\n\"\n    // part 3 : LDMIA STMDB\n    \"p3:\\n\"\n    \"   adr lr, p2_end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub r2, sp, #4\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   ite gt\\n\"\n    \"   stmdbgt r2, {r0,r2-r12, lr}\\n\"\n    \"   stmdble r2, {r0-r11, lr}\\n\"\n    \"   sub r2, r2, #52\\n\"\n    \"   mov sp, r2\\n\"\n    \"   b p4\\n\"\n    \"p3_end:\\n\"\n    \"   mov r5, sp\\n\"\n    \"   cmp r10, r12\\n\"\n    \"   itet gt\\n\"\n    \"   addgt sp, sp, #56\\n\"\n    \"   ldmiale r5, {r0-r12}\\n\"\n    \"   ldmiagt r5, {r0-r3,r5-r12, pc}\\n\"\n    \"   add sp, sp, #52\\n\"\n    \"   str r5, [sp], #4\\n\"\n    \"   blx r12\\n\"\n    \".align 2\\n\"\n    // part 4 : LDMIA STMDB\n    \"p4:\\n\"\n    \"   adr lr, p4_ending\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub r9, sp, #4\\n\"\n    \"   stmdb r9, {r0-r8,r10-r12, lr}\\n\"\n    \"   sub sp, sp, #56\\n\"\n    \"p4_end:\\n\"\n    \"   mov r2, sp\\n\"\n    \"   ldmia r2, {r0,r1,r3-r12, pc}\\n\"\n    \"   bkpt #10\\n\"\n    \".align 2\\n\"\n    \"p4_ending:\\n\"\n    \"   add sp, sp, #52\\n\"\n    \"   str r9, [sp], #4\\n\"\n    \"   b p3_end\\n\"\n    \"end:\\n\";\n\nconst char *tLdmdbStmiaTest_s =\n    // part 1 : LDMDB STMIA\n    \"p1:\\n\"\n    \"   adr lr, p1_ending\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub sp, sp, #60\\n\"\n    \"   mov r9, sp\\n\"\n    \"   stmia r9, {r0-r12,lr}\\n\"\n    \"   b p2\\n\"\n    \"p1_end:\\n\"\n    \"   add sp, sp, #56\\n\"\n    \"   mov r2, sp\\n\"\n    \"   ldmdb r2, {r0-r12,pc}\\n\"\n    \"   bkpt #10\\n\"\n    \".align 2\\n\"\n    \"p1_ending:\\n\"\n    \"   str r2, [sp], #4\\n\"\n    \"   b end\\n\"\n    // part 2 : LDMDB STMIA\n    \"p2:\\n\"\n    \"   sub sp, sp, #60\\n\"\n    \"   mov r2, sp\\n\"\n    \"   str r2, [sp, #44]\\n\"\n    \"   stmia r2, {r0,r2-r12}\\n\"\n    \"   b p3\\n\"\n    \".align 2\\n\"\n    \"p2_end:\\n\"\n    \"   add sp, sp, #52\\n\"\n    \"   mov r3, sp\\n\"\n    \"   ldmdb r3, {r0,r1,r3-r12,lr}\\n\"\n    \"   str r3, [sp], #4\\n\"\n    \"   str r1, [sp], #4\\n\"\n    \"   b p1_end\\n\"\n    // part 3 : LDMDB STMIA\n    \"p3:\\n\"\n    \"   adr lr, p2_end\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub sp, sp, #56\\n\"\n    \"   mov r2, sp\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   ite gt\\n\"\n    \"   stmiagt r2, {r0,r2-r12,lr}\\n\"\n    \"   stmiale r2, {r0-r11,lr}\\n\"\n    \"   str r2, [sp, #-4]\\n\"\n    \"   b p4\\n\"\n    \"p3_end:\\n\"\n    \"   add r5, sp, #52\\n\"\n    \"   cmp r10, r12\\n\"\n    \"   itet gt\\n\"\n    \"   addgt sp, sp, #56\\n\"\n    \"   ldmdble r5, {r0-r4,r5-r12}\\n\"\n    \"   ldmdbgt r5, {r0-r3,r5-r12,pc}\\n\"\n    \"   add sp, sp, #52\\n\"\n    \"   str r5, [sp], #4\\n\"\n    \"   blx r12\\n\"\n    \".align 2\\n\"\n    // part 4 : LDMDB STMIA\n    \"p4:\\n\"\n    \"   adr lr, p4_ending\\n\"\n    \"   eor lr, #1\\n\"\n    \"   sub sp, sp, #56\\n\"\n    \"   mov r9, sp\\n\"\n    \"   stmia r9, {r0-r8,r10-r12,lr}\\n\"\n    \"p4_end:\\n\"\n    \"   add sp, sp, #52\\n\"\n    \"   mov r2, sp\\n\"\n    \"   ldmdb r2, {r0,r1,r3-r12,pc}\\n\"\n    \"   bkpt #10\\n\"\n    \".align 2\\n\"\n    \"p4_ending:\\n\"\n    \"   str r2, [sp], #4\\n\"\n    \"   b p3_end\\n\"\n    \"end:\\n\";\n\nconst char *tLdrPCTest_s =\n    \"   mov r12, sp\\n\"\n    // test t2LDRpci positive offset\n    \".align 2\\n\"\n    \"   ldr.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldr.w r1, table1_1\\n\"\n    // test t2LDRpci positive offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle.w r2, table1_1\\n\"\n    \"   ldrgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle.w r3, table1_1\\n\"\n    \"   ldrgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    // test t2LDRpci with PC\n    \"   mov r0, #0\\n\"\n    \".align 2\\n\"\n    \"   ldr.w pc, table1_label1\\n\"\n    \"   mov r0, #1\\n\"\n    \".thumb_func\\n\"\n    \"label1:\\n\"\n    \"   mov r1, #0\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldr.w pc, table1_label2\\n\"\n    \"   mov r1, #1\\n\"\n    \".thumb_func\\n\"\n    \"label2:\\n\"\n    // test t2LDRpci with PC and cond\n    \"   mov r2, #0\\n\"\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   movle r2, #1\\n\"\n    \"   ldrgt.w pc, table1_label3\\n\"\n    \"   mov r2, #2\\n\"\n    \".thumb_func\\n\"\n    \"label3:\\n\"\n    \"   mov r3, #0\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   movle r3, #1\\n\"\n    \"   ldrgt.w pc, table1_label4\\n\"\n    \"   mov r3, #2\\n\"\n    \".thumb_func\\n\"\n    \"label4:\\n\"\n    \"   push {r0-r3}\\n\"\n    // test t2LDRi12 with PC\n    \"   mov r0, #0\\n\"\n    \".align 2\\n\"\n    \"   adr r4, table1_label5\\n\"\n    \"   ldr.w pc, [r4]\\n\"\n    \"   mov r0, #1\\n\"\n    \".thumb_func\\n\"\n    \"label5:\\n\"\n    \"   mov r1, #0\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   adr r4, table1_label6\\n\"\n    \"   ldr.w pc, [r4]\\n\"\n    \"   mov r1, #1\\n\"\n    \".thumb_func\\n\"\n    \"label6:\\n\"\n    // test t2LDRi12 with PC and cond\n    \"   mov r2, #0\\n\"\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   itee le\\n\"\n    \"   movle r3, #1\\n\"\n    \"   adrgt r4, table1_label7\\n\"\n    \"   ldrgt.w pc, [r4]\\n\"\n    \"   mov r2, #2\\n\"\n    \".thumb_func\\n\"\n    \"label7:\\n\"\n    \"   mov r3, #0\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   itee le\\n\"\n    \"   movle r3, #1\\n\"\n    \"   adrgt r4, table1_label8\\n\"\n    \"   ldrgt.w pc, [r4]\\n\"\n    \"   mov r3, #2\\n\"\n    \".thumb_func\\n\"\n    \"label8:\\n\"\n    \"   push {r0-r3}\\n\"\n    // test tLDRpci\n    \".align 2\\n\"\n    \"   ldr r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldr r1, table1_1\\n\"\n    // test tLDRpci with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle r2, table1_1\\n\"\n    \"   ldrgt r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle r3, table1_1\\n\"\n    \"   ldrgt r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test2\\n\"\n    \".align 2\\n\"\n    \"table1_0:\\n\"\n    \"   .word 0x43424140\\n\"\n    \"table1_1:\\n\"\n    \"   .word 0x47464544\\n\"\n    \"table1_2:\\n\"\n    \"   .word 0x4b4a4948\\n\"\n    \"table1_4:\\n\"\n    \"   .word 0x4f4e4d4c\\n\"\n    \"table1_label1:\\n\"\n    \"   .word label1()\\n\"\n    \"table1_label2:\\n\"\n    \"   .word label2()\\n\"\n    \"table1_label3:\\n\"\n    \"   .word label3()\\n\"\n    \"table1_label4:\\n\"\n    \"   .word label4()\\n\"\n    \"table1_label5:\\n\"\n    \"   .word label5()\\n\"\n    \"table1_label6:\\n\"\n    \"   .word label6()\\n\"\n    \"table1_label7:\\n\"\n    \"   .word label7()\\n\"\n    \"table1_label8:\\n\"\n    \"   .word label8()\\n\"\n    \"table1_label9:\\n\"\n    \"   .word label9()\\n\"\n    \"table1_label10:\\n\"\n    \"   .word label10()\\n\"\n    \"table1_label11:\\n\"\n    \"   .word label11()\\n\"\n    \"table1_label12:\\n\"\n    \"   .word label12()\\n\"\n    \"   .word label13()\\n\"\n    \"   .word label14()\\n\"\n    \"   .word label15()\\n\"\n    \"   .word label16()\\n\"\n    // test t2LDRpci negative offset\n    \".align 2\\n\"\n    \"test2:\\n\"\n    \"   ldr.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldr.w r1, table1_1\\n\"\n    // test t2LDRpci negative offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle.w r2, table1_1\\n\"\n    \"   ldrgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle.w r3, table1_1\\n\"\n    \"   ldrgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test3\\n\"\n    // test with a offset < -256\n    \".align 8\\n\"\n    \"   nop\\n\"\n    \".align 8\\n\"\n    // test t2LDRpci negative offset < -256\n    \"test3:\\n\"\n    \"   ldr.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldr.w r1, table1_1\\n\"\n    // test t2LDRpci negative offset < -256 with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle.w r2, table1_1\\n\"\n    \"   ldrgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrle.w r3, table1_1\\n\"\n    \"   ldrgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    // test t2LDRpci negative offset < -256 with PC\n    \"   mov r0, #0\\n\"\n    \".align 2\\n\"\n    \"   ldr.w pc, table1_label9\\n\"\n    \"   mov r0, #1\\n\"\n    \".thumb_func\\n\"\n    \"label9:\\n\"\n    \"   mov r1, #0\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldr.w pc, table1_label10\\n\"\n    \"   mov r1, #1\\n\"\n    \".thumb_func\\n\"\n    \"label10:\\n\"\n    // test t2LDRpci negative offset < -256 with PC and cond\n    \"   mov r2, #0\\n\"\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   movle r2, #1\\n\"\n    \"   ldrgt.w pc, table1_label11\\n\"\n    \"   mov r2, #2\\n\"\n    \".thumb_func\\n\"\n    \"label11:\\n\"\n    \"   mov r3, #0\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   movle r3, #1\\n\"\n    \"   ldrgt.w pc, table1_label12\\n\"\n    \"   mov r3, #2\\n\"\n    \".thumb_func\\n\"\n    \"label12:\\n\"\n    \"   push {r0-r3}\\n\"\n    // test t2LDRs with PC\n    \"   mov r0, #0\\n\"\n    \"   adr r4, table1_label1\\n\"\n    \"   mov r5, #12\\n\"\n    \".align 2\\n\"\n    \"   ldr.w pc, [r4, r5, lsl #2]\\n\"\n    \"   mov r0, #1\\n\"\n    \".thumb_func\\n\"\n    \"label13:\\n\"\n    \"   mov r1, #0\\n\"\n    \"   mov r5, #13\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldr.w pc, [r4, r5, lsl #2]\\n\"\n    \"   mov r1, #1\\n\"\n    \".thumb_func\\n\"\n    \"label14:\\n\"\n    // test t2LDRs with PC and cond\n    \"   mov r2, #0\\n\"\n    \"   mov r5, #14\\n\"\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   movle r3, #1\\n\"\n    \"   ldrgt.w pc, [r4, r5, lsl #2]\\n\"\n    \"   mov r2, #2\\n\"\n    \".thumb_func\\n\"\n    \"label15:\\n\"\n    \"   mov r3, #0\\n\"\n    \"   mov r5, #15\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   movle r3, #1\\n\"\n    \"   ldrgt.w pc, [r4, r5, lsl #2]\\n\"\n    \"   mov r3, #2\\n\"\n    \".thumb_func\\n\"\n    \"label16:\\n\"\n    \"   push {r0-r3}\\n\"\n    \".align 2\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tLdrbPCTest_s =\n    \"   mov r12, sp\\n\"\n    // test t2LDRBpci positive offset\n    \".align 2\\n\"\n    \"   ldrb.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrb.w r1, table1_1\\n\"\n    // test t2LDRBpci positive offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrble.w r2, table1_1\\n\"\n    \"   ldrbgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrble.w r3, table1_1\\n\"\n    \"   ldrbgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test2\\n\"\n    \".align 2\\n\"\n    \"table1_0:\\n\"\n    \"   .word 0x43424140\\n\"\n    \"table1_1:\\n\"\n    \"   .word 0x47464544\\n\"\n    \"table1_2:\\n\"\n    \"   .word 0x4b4a4948\\n\"\n    \"table1_4:\\n\"\n    \"   .word 0x4f4e4d4c\\n\"\n    // test t2LDRBpci negative offset\n    \".align 2\\n\"\n    \"test2:\\n\"\n    \"   ldrb.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrb.w r1, table1_1\\n\"\n    // test t2LDRBpci negative offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrble.w r2, table1_1\\n\"\n    \"   ldrbgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrble.w r3, table1_1\\n\"\n    \"   ldrbgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test3\\n\"\n    // test with a offset < -256\n    \".align 8\\n\"\n    \"   nop\\n\"\n    \".align 8\\n\"\n    // test t2LDRBpci negative offset < -256\n    \"test3:\\n\"\n    \"   ldrb.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrb.w r1, table1_1\\n\"\n    // test t2LDRBpci negative offset < -256 with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrble.w r2, table1_1\\n\"\n    \"   ldrbgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrble.w r3, table1_1\\n\"\n    \"   ldrbgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \".align 2\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tLdrdPCTest_s =\n    \"   mov r12, sp\\n\"\n    // test t2LDRDi8 positive offset\n    \".align 2\\n\"\n    \"   ldrd r0, r4, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrd r1, r5, table1_1\\n\"\n    // test t2LDRDi8 positive offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrdle r2, r6, table1_1\\n\"\n    \"   ldrdgt r2, r6, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrdle r3, r7, table1_1\\n\"\n    \"   ldrdgt r3, r7, table1_2\\n\"\n    \"   push {r0-r7}\\n\"\n    \"   b test2\\n\"\n    \".align 2\\n\"\n    \"table1_0:\\n\"\n    \"   .word 0x43424140\\n\"\n    \"table1_1:\\n\"\n    \"   .word 0x47464544\\n\"\n    \"table1_2:\\n\"\n    \"   .word 0x4b4a4948\\n\"\n    \"table1_4:\\n\"\n    \"   .word 0x4f4e4d4c\\n\"\n    // test t2LDRDi8 negative offset\n    \".align 2\\n\"\n    \"test2:\\n\"\n    \"   ldrd r0, r2, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrd r1, r3, table1_1\\n\"\n    // test t2LDRDi8 negative offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrdle r4, r6, table1_1\\n\"\n    \"   ldrdgt r4, r6, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrdle r5, r7, table1_1\\n\"\n    \"   ldrdgt r5, r7, table1_2\\n\"\n    \"   push {r0-r7}\\n\"\n    \".align 2\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tLdrhPCTest_s =\n    \"   mov r12, sp\\n\"\n    // test t2LDRHpci positive offset\n    \".align 2\\n\"\n    \"   ldrh.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrh.w r1, table1_1\\n\"\n    // test t2LDRHpci positive offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrhle.w r2, table1_1\\n\"\n    \"   ldrhgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrhle.w r3, table1_1\\n\"\n    \"   ldrhgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test2\\n\"\n    \".align 2\\n\"\n    \"table1_0:\\n\"\n    \"   .word 0x43424140\\n\"\n    \"table1_1:\\n\"\n    \"   .word 0x47464544\\n\"\n    \"table1_2:\\n\"\n    \"   .word 0x4b4a4948\\n\"\n    \"table1_4:\\n\"\n    \"   .word 0x4f4e4d4c\\n\"\n    // test t2LDRHpci negative offset\n    \".align 2\\n\"\n    \"test2:\\n\"\n    \"   ldrh.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrh.w r1, table1_1\\n\"\n    // test t2LDRHpci negative offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrhle.w r2, table1_1\\n\"\n    \"   ldrhgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrhle.w r3, table1_1\\n\"\n    \"   ldrhgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test3\\n\"\n    // test with a offset < -256\n    \".align 8\\n\"\n    \"   nop\\n\"\n    \".align 8\\n\"\n    // test t2LDRHpci negative offset < -256\n    \"test3:\\n\"\n    \"   ldrh.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrh.w r1, table1_1\\n\"\n    // test t2LDRHpci negative offset < -256 with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrhle.w r2, table1_1\\n\"\n    \"   ldrhgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrhle.w r3, table1_1\\n\"\n    \"   ldrhgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \".align 2\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tLdrsbPCTest_s =\n    \"   mov r12, sp\\n\"\n    // test t2LDRSBpci positive offset\n    \".align 2\\n\"\n    \"   ldrsb.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrsb.w r1, table1_1\\n\"\n    // test t2LDRSBpci positive offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrsble.w r2, table1_1\\n\"\n    \"   ldrsbgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrsble.w r3, table1_1\\n\"\n    \"   ldrsbgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test2\\n\"\n    \".align 2\\n\"\n    \"table1_0:\\n\"\n    \"   .word 0x83828180\\n\"\n    \"table1_1:\\n\"\n    \"   .word 0x87868584\\n\"\n    \"table1_2:\\n\"\n    \"   .word 0x8b8a8988\\n\"\n    \"table1_4:\\n\"\n    \"   .word 0x8f8e8d8c\\n\"\n    // test t2LDRSBpci negative offset\n    \".align 2\\n\"\n    \"test2:\\n\"\n    \"   ldrsb.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrsb.w r1, table1_1\\n\"\n    // test t2LDRSBpci negative offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrsble.w r2, table1_1\\n\"\n    \"   ldrsbgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrsble.w r3, table1_1\\n\"\n    \"   ldrsbgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test3\\n\"\n    // test with a offset < -256\n    \".align 8\\n\"\n    \"   nop\\n\"\n    \".align 8\\n\"\n    // test t2LDRSBpci negative offset < -256\n    \"test3:\\n\"\n    \"   ldrsb.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrsb.w r1, table1_1\\n\"\n    // test t2LDRSBpci negative offset < -256 with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrsble.w r2, table1_1\\n\"\n    \"   ldrsbgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrsble.w r3, table1_1\\n\"\n    \"   ldrsbgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \".align 2\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tLdrshPCTest_s =\n    \"   mov r12, sp\\n\"\n    // test t2LDRSHpci positive offset\n    \".align 2\\n\"\n    \"   ldrsh.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrsh.w r1, table1_1\\n\"\n    // test t2LDRSHpci positive offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrshle.w r2, table1_1\\n\"\n    \"   ldrshgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrshle.w r3, table1_1\\n\"\n    \"   ldrshgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test2\\n\"\n    \".align 2\\n\"\n    \"table1_0:\\n\"\n    \"   .word 0x83828180\\n\"\n    \"table1_1:\\n\"\n    \"   .word 0x87868584\\n\"\n    \"table1_2:\\n\"\n    \"   .word 0x8b8a8988\\n\"\n    \"table1_4:\\n\"\n    \"   .word 0x8f8e8d8c\\n\"\n    // test t2LDRSHpci negative offset\n    \".align 2\\n\"\n    \"test2:\\n\"\n    \"   ldrsh.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrsh.w r1, table1_1\\n\"\n    // test t2LDRSHpci negative offset with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrshle.w r2, table1_1\\n\"\n    \"   ldrshgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrshle.w r3, table1_1\\n\"\n    \"   ldrshgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \"   b test3\\n\"\n    // test with a offset < -256\n    \".align 8\\n\"\n    \"   nop\\n\"\n    \".align 8\\n\"\n    // test t2LDRSHpci negative offset < -256\n    \"test3:\\n\"\n    \"   ldrsh.w r0, table1_1\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   ldrsh.w r1, table1_1\\n\"\n    // test t2LDRSHpci negative offset < -256 with cond\n    \".align 2\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrshle.w r2, table1_1\\n\"\n    \"   ldrshgt.w r2, table1_2\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ite le\\n\"\n    \"   ldrshle.w r3, table1_1\\n\"\n    \"   ldrshgt.w r3, table1_2\\n\"\n    \"   push {r0-r3}\\n\"\n    \".align 2\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tMovPCTest_s =\n    \"   mov r12, sp\\n\"\n    \"   mov r0, #42\\n\"\n    \"   mov r1, #74\\n\"\n    \".align 2\\n\"\n    // tMOVr pc, pc\n    \"   mov pc, pc\\n\"\n    \"   b t1\\n\"\n    \"   push {r0}\\n\"\n    \"   b t2\\n\"\n    \"t1:\\n\"\n    \"   push {r1}\\n\"\n    \"t2:\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   mov pc, pc\\n\"\n    \"   b t3\\n\"\n    \"   push {r0}\\n\"\n    \"   b t4\\n\"\n    \"t3:\\n\"\n    \"   push {r1}\\n\"\n    \"t4:\\n\"\n    \".align 2\\n\"\n    // tMOVr pc, pc with cond\n    \"   cmp r11, r12\\n\"\n    \"   it le\\n\"\n    \"   movle pc, pc\\n\"\n    \"   b t5\\n\"\n    \"   push {r0}\\n\"\n    \"   b t6\\n\"\n    \"t5:\\n\"\n    \"   push {r1}\\n\"\n    \"t6:\\n\"\n    \".align 2\\n\"\n    \"   nop\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   it le\\n\"\n    \"   movle pc, pc\\n\"\n    \"   b t7\\n\"\n    \"   push {r0}\\n\"\n    \"   b t8\\n\"\n    \"t7:\\n\"\n    \"   push {r1}\\n\"\n    \"t8:\\n\"\n    \".align 2\\n\"\n    // tMOVr pc, <reg>\n    \"   adr r7, t9\\n\"\n    \"   mov pc, r7\\n\"\n    \"   push {r0}\\n\"\n    \"   b t10\\n\"\n    \".align 2\\n\"\n    \"t9:\\n\"\n    \"   push {r1}\\n\"\n    \".align 2\\n\"\n    \"t10:\\n\"\n    \"   nop\\n\"\n    \"   adr r7, t11\\n\"\n    \"   mov pc, pc\\n\"\n    \"   push {r0}\\n\"\n    \"   b t12\\n\"\n    \".align 2\\n\"\n    \"t11:\\n\"\n    \"   push {r1}\\n\"\n    \".align 2\\n\"\n    \"t12:\\n\"\n    // tMOVr pc, <reg> with cond\n    \"   cmp r11, r12\\n\"\n    \"   adr r7, t13\\n\"\n    \"   it le\\n\"\n    \"   movle pc, r7\\n\"\n    \"   push {r0}\\n\"\n    \"   b t14\\n\"\n    \".align 2\\n\"\n    \"t13:\\n\"\n    \"   push {r1}\\n\"\n    \".align 2\\n\"\n    \"t14:\\n\"\n    \"   nop\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   adr r7, t15\\n\"\n    \"   it le\\n\"\n    \"   movle pc, r7\\n\"\n    \"   push {r0}\\n\"\n    \"   b t16\\n\"\n    \".align 2\\n\"\n    \"t15:\\n\"\n    \"   push {r1}\\n\"\n    \".align 2\\n\"\n    \"t16:\\n\"\n    // tMOVr <reg>, pc\n    \"   mov r0, pc\\n\"\n    \"   mov r1, pc\\n\"\n    \"   cmp r11, r12\\n\"\n    \".align 2\\n\"\n    \"   ittee le\\n\"\n    \"   movle r2, pc\\n\"\n    \"   movle r3, pc\\n\"\n    \"   movgt r2, pc\\n\"\n    \"   movgt r3, pc\\n\"\n    \"   push {r0-r3}\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tTBBTest_s =\n    \"   mov r12, sp\\n\"\n    // test 1: aligned TBB pc\n    \"   mov r0, r1\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r2\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot1\\n\"\n    \"   b t2\\n\"\n    \"foot1:\\n\"\n    \"   and r0, 7\\n\"\n    \".align 4\\n\"\n    \"   tbb [pc, r0]\\n\"\n    \".byte 6\\n\"\n    \".byte 14\\n\"\n    \".byte 22\\n\"\n    \".byte 30\\n\"\n    \".byte 38\\n\"\n    \".byte 46\\n\"\n    \".byte 54\\n\"\n    \".byte 62\\n\"\n    \".align 4\\n\"\n    // target tbb 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t2:\\n\"\n    // test 2: unaligned TBB pc\n    \"   mov r0, r1\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r2\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot2\\n\"\n    \"   b t3\\n\"\n    \"foot2:\\n\"\n    \"   and r0, 7\\n\"\n    \".align 4\\n\"\n    \"   nop\\n\"\n    \"   tbb [pc, r0]\\n\"\n    \".byte 5\\n\"\n    \".byte 13\\n\"\n    \".byte 21\\n\"\n    \".byte 29\\n\"\n    \".byte 37\\n\"\n    \".byte 45\\n\"\n    \".byte 53\\n\"\n    \".byte 61\\n\"\n    \".align 4\\n\"\n    // target tbb 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t3:\\n\"\n    // test 3: aligned TBB <reg>\n    \"   mov r0, r2\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot3\\n\"\n    \"   b t4\\n\"\n    \".align 2\\n\"\n    \"tablet3:\\n\"\n    \".byte 6\\n\"\n    \".byte 14\\n\"\n    \".byte 22\\n\"\n    \".byte 30\\n\"\n    \".byte 38\\n\"\n    \".byte 46\\n\"\n    \".byte 54\\n\"\n    \".byte 62\\n\"\n    \"foot3:\\n\"\n    \"   and r0, 7\\n\"\n    \"   adr r1, tablet3\\n\"\n    \".align 4\\n\"\n    \"   tbb [r1, r0]\\n\"\n    \".align 4\\n\"\n    // target tbb 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t4:\\n\"\n    // test 4: aligned TBB <reg>\n    \"   mov r0, r2\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot4\\n\"\n    \"   b t5\\n\"\n    \".align 2\\n\"\n    \"tablet4:\\n\"\n    \".byte 5\\n\"\n    \".byte 13\\n\"\n    \".byte 21\\n\"\n    \".byte 29\\n\"\n    \".byte 37\\n\"\n    \".byte 45\\n\"\n    \".byte 53\\n\"\n    \".byte 61\\n\"\n    \"foot4:\\n\"\n    \"   and r0, 7\\n\"\n    \"   adr r1, tablet4\\n\"\n    \".align 4\\n\"\n    \"   nop\\n\"\n    \"   tbb [r1, r0]\\n\"\n    \".align 4\\n\"\n    // target tbb 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t5:\\n\"\n    // test 5: TBB <reg> cond\n    \"   mov r0, r2\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot5\\n\"\n    \"   b end\\n\"\n    \".align 2\\n\"\n    \"tablet5_1:\\n\"\n    \".byte 3\\n\"\n    \".byte 11\\n\"\n    \".byte 19\\n\"\n    \".byte 27\\n\"\n    \".byte 35\\n\"\n    \".byte 43\\n\"\n    \".byte 51\\n\"\n    \".byte 59\\n\"\n    \".align 2\\n\"\n    \"tablet5_2:\\n\"\n    \".byte 25\\n\"\n    \".byte 57\\n\"\n    \".byte 33\\n\"\n    \".byte 9\\n\"\n    \".byte 41\\n\"\n    \".byte 1\\n\"\n    \".byte 17\\n\"\n    \".byte 49\\n\"\n    \"foot5:\\n\"\n    \"   and r0, 7\\n\"\n    \"   adr r1, tablet5_1\\n\"\n    \"   cmp r10, r11\\n\"\n    \".align 4\\n\"\n    \"   ite ge\\n\"\n    \"   adrge.w r1, tablet5_2\\n\"\n    \"   tbblt [r1, r0]\\n\"\n    \"   tbb [r1, r0]\\n\"\n    \".align 4\\n\"\n    // target tbb 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbb 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tTBHTest_s =\n    \"   mov r12, sp\\n\"\n    // test 1: aligned TBH pc\n    \"   mov r0, r1\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r2\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot1\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot1\\n\"\n    \"   b t2\\n\"\n    \"foot1:\\n\"\n    \"   and r0, 7\\n\"\n    \".align 4\\n\"\n    \"   tbh [pc, r0, lsl #1]\\n\"\n    \".hword 14\\n\"\n    \".hword 22\\n\"\n    \".hword 30\\n\"\n    \".hword 38\\n\"\n    \".hword 46\\n\"\n    \".hword 54\\n\"\n    \".hword 62\\n\"\n    \".hword 70\\n\"\n    \".align 4\\n\"\n    // target tbh 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t2:\\n\"\n    // test 2: unaligned TBH pc\n    \"   mov r0, r1\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r2\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot2\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot2\\n\"\n    \"   b t3\\n\"\n    \"foot2:\\n\"\n    \"   and r0, 7\\n\"\n    \".align 4\\n\"\n    \"   nop\\n\"\n    \"   tbh [pc, r0, lsl #1]\\n\"\n    \".hword 13\\n\"\n    \".hword 21\\n\"\n    \".hword 29\\n\"\n    \".hword 37\\n\"\n    \".hword 45\\n\"\n    \".hword 53\\n\"\n    \".hword 61\\n\"\n    \".hword 69\\n\"\n    \".align 4\\n\"\n    // target tbh 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t3:\\n\"\n    // test 3: aligned TBH <reg>\n    \"   mov r0, r2\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot3\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot3\\n\"\n    \"   b t4\\n\"\n    \".align 2\\n\"\n    \"tablet3:\\n\"\n    \".hword 6\\n\"\n    \".hword 14\\n\"\n    \".hword 22\\n\"\n    \".hword 30\\n\"\n    \".hword 38\\n\"\n    \".hword 46\\n\"\n    \".hword 54\\n\"\n    \".hword 62\\n\"\n    \"foot3:\\n\"\n    \"   and r0, 7\\n\"\n    \"   adr r1, tablet3\\n\"\n    \".align 4\\n\"\n    \"   tbh [r1, r0, lsl #1]\\n\"\n    \".align 4\\n\"\n    // target tbh 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t4:\\n\"\n    // test 4: aligned TBH <reg>\n    \"   mov r0, r2\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot4\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot4\\n\"\n    \"   b t5\\n\"\n    \".align 2\\n\"\n    \"tablet4:\\n\"\n    \".hword 5\\n\"\n    \".hword 13\\n\"\n    \".hword 21\\n\"\n    \".hword 29\\n\"\n    \".hword 37\\n\"\n    \".hword 45\\n\"\n    \".hword 53\\n\"\n    \".hword 61\\n\"\n    \"foot4:\\n\"\n    \"   and r0, 7\\n\"\n    \"   adr r1, tablet4\\n\"\n    \".align 4\\n\"\n    \"   nop\\n\"\n    \"   tbh [r1, r0, lsl #1]\\n\"\n    \".align 4\\n\"\n    // target tbh 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"t5:\\n\"\n    // test 5: TBH <reg> cond\n    \"   mov r0, r2\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r3\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r4\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r5\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r6\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r7\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r8\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r9\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r10\\n\"\n    \"   bl foot5\\n\"\n    \"   mov r0, r11\\n\"\n    \"   bl foot5\\n\"\n    \"   b end\\n\"\n    \".align 2\\n\"\n    \"tablet5_1:\\n\"\n    \".hword 3\\n\"\n    \".hword 11\\n\"\n    \".hword 19\\n\"\n    \".hword 27\\n\"\n    \".hword 35\\n\"\n    \".hword 43\\n\"\n    \".hword 51\\n\"\n    \".hword 59\\n\"\n    \".align 2\\n\"\n    \"tablet5_2:\\n\"\n    \".hword 25\\n\"\n    \".hword 57\\n\"\n    \".hword 33\\n\"\n    \".hword 9\\n\"\n    \".hword 41\\n\"\n    \".hword 1\\n\"\n    \".hword 17\\n\"\n    \".hword 49\\n\"\n    \"foot5:\\n\"\n    \"   and r0, 7\\n\"\n    \"   adr r1, tablet5_1\\n\"\n    \"   cmp r10, r11\\n\"\n    \".align 4\\n\"\n    \"   ite ge\\n\"\n    \"   adrge.w r1, tablet5_2\\n\"\n    \"   tbhlt [r1, r0, lsl #1]\\n\"\n    \"   tbh [r1, r0, lsl #1]\\n\"\n    \".align 4\\n\"\n    // target tbh 1\n    \"   mov r0, #1\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 2\n    \"   mov r0, #2\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 3\n    \"   mov r0, #3\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 4\n    \"   mov r0, #4\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 5\n    \"   mov r0, #5\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 6\n    \"   mov r0, #6\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 7\n    \"   mov r0, #7\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \".align 4\\n\"\n    // target tbh 8\n    \"   mov r0, #8\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tITCondTest_s =\n    \"   mov r12, sp\\n\"\n    \"   b test_start\\n\"\n    \".align 4\\n\"\n    \".thumb_func\\n\"\n    \"push_apsr:\\n\"\n    \"   mrs r0, APSR\\n\"\n    \"   push {r0}\\n\"\n    \"   bx lr\\n\"\n    \"test_start:\\n\"\n    \"   cmp r11, r12\\n\"\n    \"   bl push_apsr\\n\"\n    \"   adds r1, #12\\n\"\n    \"   it al\\n\"\n    \"   addal r2, #12\\n\"\n    \"   bl push_apsr\\n\"\n    // test change flags in ITBlock\n    \"   mov r0, #0\\n\"\n    \"   cmp r10, r11\\n\"\n    \"   ittee gt\\n\"\n    \"   cmpgt r11, r10\\n\"\n    \"   movgt r4, r0\\n\"\n    \"   cmple r11, r10\\n\"\n    \"   movle r5, r0\\n\"\n    \"   push {r4, r5}\\n\"\n    \"   bl push_apsr\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n\nconst char *tldrexTest_s =\n    \"   mov r12, sp\\n\"\n    \"   mov r2, #0\\n\"\n    \"loopmemset:\\n\"\n    \"   strh r2, [r11, r2, lsl #1]\\n\"\n    \"   add r2, #1\\n\"\n    \"   cmp r2, 2048\\n\"\n    \"   bne loopmemset\\n\"\n    // align 4 r11 (to support ldrexd)\n    \"   orr r11, r11, 0xf\\n\"\n    \"   add r11, r11, 1\\n\"\n    // test 1\n    \"   ldrex r0, [r11]\\n\"\n    \"   mov r1, #0xff\\n\"\n    \"   strex r2, r1, [r11]\\n\"\n    \"   ldr r1, [r11]\\n\"\n    \"   push {r0, r1, r2}\\n\"\n    // test 2\n    \"   ldrex r0, [r11, #256]\\n\"\n    \"   mov r1, #0xfa7\\n\"\n    \"   add r10, r11, #256\\n\"\n    \"   strex r2, r1, [r10]\\n\"\n    \"   ldr r1, [r11, #256]\\n\"\n    \"   push {r0, r1, r2}\\n\"\n    // test 3\n    \"   ldrex r0, [r11]\\n\"\n    \"   mov r1, #0xfa8\\n\"\n    \"   strex r2, r1, [r11, #300]\\n\"\n    \"   ldr r1, [r11, #300]\\n\"\n    \"   push {r0, r1, r2}\\n\"\n    // test 4\n    \"   add r10, r11, #2048\\n\"\n    \"   ldrex r0, [r11]\\n\"\n    \"   ldrex r1, [r10]\\n\"\n    \"   mov r2, #0xfa8\\n\"\n    \"   mov r3, #0xc58\\n\"\n    \"   strex r4, r2, [r11]\\n\"\n    \"   strex r5, r3, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 5\n    \"   add r10, r11, #3096\\n\"\n    \"   ldrex r0, [r11]\\n\"\n    \"   ldrex r1, [r10]\\n\"\n    \"   mov r2, #0x1a4\\n\"\n    \"   mov r3, #0x453\\n\"\n    \"   strex r4, r2, [r10]\\n\"\n    \"   strex r5, r3, [r11]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 6\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   add r10, r11, #3096\\n\"\n    \"   cmp r8, r9\\n\"\n    \"   ite le\\n\"\n    \"   ldrexle r0, [r11]\\n\"\n    \"   ldrexgt r1, [r10]\\n\"\n    \"   mov r2, #0x58\\n\"\n    \"   mov r3, #0x761\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   ite le\\n\"\n    \"   strexle r5, r3, [r11]\\n\"\n    \"   strexgt r4, r2, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 7\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   add r10, r11, #3080\\n\"\n    \"   cmp r8, r9\\n\"\n    \"   ite le\\n\"\n    \"   ldrexle r0, [r11]\\n\"\n    \"   ldrexgt r1, [r10]\\n\"\n    \"   mov r2, #0x146\\n\"\n    \"   mov r3, #0x9de\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   ite le\\n\"\n    \"   strexle r4, r2, [r10]\\n\"\n    \"   strexgt r5, r3, [r11]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 8\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   add r10, r11, #3000\\n\"\n    \"   cmp r9, r8\\n\"\n    \"   ite le\\n\"\n    \"   ldrexhle r0, [r11]\\n\"\n    \"   ldrexbgt r1, [r10]\\n\"\n    \"   mov r2, #0xb5\\n\"\n    \"   mov r3, #0xea\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   ite le\\n\"\n    \"   strexhle r5, r3, [r11]\\n\"\n    \"   strexbgt r4, r2, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    // test 9\n    \"   mov r0, #0\\n\"\n    \"   mov r1, #0\\n\"\n    \"   add r10, r11, #1032\\n\"\n    \"   cmp r9, r8\\n\"\n    \"   ite le\\n\"\n    \"   ldrexdle r0, r8, [r11]\\n\"\n    \"   ldrexbgt r1, [r10]\\n\"\n    \"   mov r2, #0x78\\n\"\n    \"   mov r3, #0x46d\\n\"\n    \"   mov r4, #2\\n\"\n    \"   mov r5, #2\\n\"\n    \"   ite le\\n\"\n    \"   strexble r5, r3, [r11]\\n\"\n    \"   strexdgt r4, r9, r2, [r10]\\n\"\n    \"   ldr r6, [r11]\\n\"\n    \"   ldr r7, [r10]\\n\"\n    \"   push {r0-r7}\\n\"\n    \"end:\\n\"\n    \"   mov sp, r12\\n\";\n"
  },
  {
    "path": "test/Patch/ARM/ComparedExecutor_Thumb.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef COMPAREDEXECUTOR_THUMB_H\n#define COMPAREDEXECUTOR_THUMB_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n#include \"TestSetup/ShellcodeTester.h\"\n\n#define CPU_CPU \"cortex-a57\"\n#define CPU_MATTRS \"neon\", \"thumb2\", \"v7\"\n\nextern const char *tGPRSave_s;\nextern const char *tGPRShuffle_s;\nextern const char *tRelativeAddressing_s;\nextern const char *tFibonacciRecursion_s;\nextern const char *tBranchCondTest_s;\nextern const char *tBranchLinkCondTest_s;\nextern const char *tBranchRegisterCondTest_s;\nextern const char *tPushPopTest_s;\nextern const char *tLdmiaStmdbWbackTest_s;\nextern const char *tLdmdbStmiaWbackTest_s;\nextern const char *tLdmiaStmdbTest_s;\nextern const char *tLdmdbStmiaTest_s;\nextern const char *tLdrPCTest_s;\nextern const char *tLdrbPCTest_s;\nextern const char *tLdrdPCTest_s;\nextern const char *tLdrhPCTest_s;\nextern const char *tLdrsbPCTest_s;\nextern const char *tLdrshPCTest_s;\nextern const char *tMovPCTest_s;\nextern const char *tTBBTest_s;\nextern const char *tTBHTest_s;\nextern const char *tITCondTest_s;\nextern const char *tldrexTest_s;\n// extern const char *tConditionalBranching_s;\n// extern const char *tStackTricks_s;\n// extern const char *tSTLDMIA_s;\n// extern const char *tSTLDMIB_s;\n// extern const char *tSTLDMDA_s;\n// extern const char *tSTLDMDB_s;\n// extern const char *tSTMDB_LDMIA_post_s;\n// extern const char *tSTMDA_LDMIB_post_s;\n// extern const char *tSTMIB_LDMDA_post_s;\n// extern const char *tSTMIA_LDMDB_post_s;\n\nclass ComparedExecutor_Thumb : public ShellcodeTester {\n\npublic:\n  ComparedExecutor_Thumb() : ShellcodeTester(CPU_CPU, {CPU_MATTRS}) {}\n\n  QBDI::Context jitExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                        llvm::sys::MemoryBlock &stack);\n\n  QBDI::Context realExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                         llvm::sys::MemoryBlock &stack);\n\n  InMemoryObject compileWithContextSwitch(const char *source);\n\n  void initContext(QBDI::Context &ctx);\n};\n\n#endif\n"
  },
  {
    "path": "test/Patch/ARM/Instr_Test_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"Patch/Instr_Test.h\"\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STLDMIA\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STLDMIA_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STLDMIB\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STLDMIB_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STLDMDA\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STLDMDA_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STLDMDB\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STLDMDB_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STMDB_LDMIA_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STMDB_LDMIA_post_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STMDA_LDMIB_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STMDA_LDMIB_post_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STMIB_LDMDA_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STMIB_LDMDA_post_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-STMIA_LDMDB_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(STMIA_LDMDB_post_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-LDREXTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n  uint8_t buffer[4096] = {0};\n\n  QBDI::Context inputState;\n  initContext(inputState);\n  inputState.gprState.r11 = (QBDI::rword)&buffer;\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(LDREXTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n"
  },
  {
    "path": "test/Patch/ARM/Instr_Test_Thumb.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Patch/ARM/Instr_Test_Thumb.h\"\n#include <catch2/catch_test_macros.hpp>\n\nQBDI::VMAction incrementThumbPre(QBDI::VMInstanceRef vm,\n                                 QBDI::GPRState *gprState,\n                                 QBDI::FPRState *fprState, void *data) {\n  *((uint64_t *)data) = *((uint64_t *)data) + 1;\n\n  // verify instAnalysis\n  const QBDI::InstAnalysis *ana =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION);\n  CHECK(((gprState->pc & 1) == 1) == (ana->cpuMode == QBDI::CPUMode::Thumb));\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VMAction incrementThumbPost(QBDI::VMInstanceRef vm,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n  *((uint64_t *)data) = *((uint64_t *)data) + 1;\n\n  // verify instAnalysis\n  const QBDI::InstAnalysis *ana =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION);\n  if (not ana->affectControlFlow) {\n    CHECK(((gprState->pc & 1) == 1) == (ana->cpuMode == QBDI::CPUMode::Thumb));\n  }\n\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tGPRSave\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tGPRSave_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tGPRShuffle\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tGPRShuffle_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tFibonacciRecursion\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n  inputState.gprState.r0 = (rand() % 20) + 2;\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tFibonacciRecursion_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tRelativeAddressing\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tRelativeAddressing_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tBranchCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tBranchCondTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tBranchLinkCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tBranchLinkCondTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tBranchRegisterCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tBranchRegisterCondTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  CHECK(inputState.gprState.r10 == 0);\n  CHECK(inputState.gprState.r11 == 0);\n  CHECK(inputState.gprState.r12 == 0);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tPushPopTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tPushPopTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdmiaStmdbWbackTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdmiaStmdbWbackTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdmdbStmiaWbackTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdmdbStmiaWbackTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdmiaStmdbTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdmiaStmdbTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdmdbStmiaTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdmdbStmiaTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdrPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdrPCTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdrbPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdrbPCTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdrdPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdrdPCTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdrhPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdrhPCTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdrsbPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdrsbPCTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tLdrshPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tLdrshPCTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tMovPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tMovPCTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tTBBTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tTBBTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tTBHTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tTBHTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tITCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tITCondTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test_Thumb, \"Instr_Test-tldrexTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n  uint8_t buffer[4096] = {0};\n\n  QBDI::Context inputState;\n  initContext(inputState);\n  inputState.gprState.r11 = (QBDI::rword)&buffer;\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, incrementThumbPre, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, incrementThumbPost, (void *)&count2);\n\n  comparedExec(tldrexTest_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n"
  },
  {
    "path": "test/Patch/ARM/Instr_Test_Thumb.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTR_TEST_THUMB_H\n#define INSTR_TEST_THUMB_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"QBDI/Platform.h\"\n#include \"Patch/Utils.h\"\n\n#include \"ComparedExecutor_Thumb.h\"\n\nclass Instr_Test_Thumb : public ComparedExecutor_Thumb {};\n\nQBDI::VMAction increment(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                         QBDI::FPRState *fprState, void *data);\n\n#endif\n"
  },
  {
    "path": "test/Patch/ARM/LLVMOperandInfo_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <set>\n\n#include <catch2/catch_test_macros.hpp>\n#include <stdio.h>\n\n#include \"ARMInstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"TestSetup/LLVMTestEnv.h\"\n#include \"Patch/InstInfo.h\"\n\nclass LLVMOperandInfoCheck : public LLVMTestEnv {\nprotected:\n  void checkPredicate(QBDI::CPUMode cpuMode);\n  void checkTIEDOperand(QBDI::CPUMode cpuMode);\n};\n\n// LLVM MCInstrDesc doesn't have a specific OperandInfo for the condition. both\n// the condition flags and the condition register are predicate.\n//\n// This test verify that QBDI can extract the condition of an instruction based\n// on the followed properties:\n// - If the instruction doesn't not support condition, the instruction doesn't\n//    have predicate operand info\n// - The condition flags is the first predicate (real type: immediate) but\n//    marked as MCOI::Predicate with MCOI::OPERAND_UNKNOWN\n// - The register flags is the second predicate (real type: reg 0 or reg CPSR)\n// but\n//    marked as MCOI::Predicate with MCOI::OPERAND_UNKNOWN. The second\n//    predicate is just after the first one.\n\nvoid LLVMOperandInfoCheck::checkPredicate(QBDI::CPUMode cpuMode) {\n  const llvm::MCInstrInfo &MCII = getCPU(cpuMode).getMCII();\n\n  for (unsigned opcode = 0; opcode < llvm::ARM::INSTRUCTION_LIST_END;\n       opcode++) {\n\n    const llvm::MCInstrDesc &desc = MCII.get(opcode);\n    const char *mnemonic = MCII.getName(opcode).data();\n\n    // the opcode is a pseudo instruction used by LLVM internally\n    if (desc.isPseudo())\n      continue;\n\n    unsigned int predicateFirstOffset = 0;\n    unsigned int predicateNumber = 0;\n    unsigned int numCCRRegClassID = 0;\n    bool consecutivePredicate = true;\n\n    for (unsigned int opn = 0; opn < desc.getNumOperands(); opn++) {\n      const llvm::MCOperandInfo &opInfo = desc.operands()[opn];\n      if (opInfo.isPredicate()) {\n        CHECK(opInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN);\n        if (predicateNumber == 0) {\n          predicateFirstOffset = opn;\n          predicateNumber++;\n        } else {\n          consecutivePredicate &=\n              (opn == predicateFirstOffset + predicateNumber);\n          predicateNumber++;\n        }\n      }\n      if (opInfo.RegClass == llvm::ARM::CCRRegClassID) {\n        CHECK(opInfo.isOptionalDef());\n        numCCRRegClassID++;\n      }\n    }\n\n    if (predicateNumber == 0) {\n      continue;\n    }\n\n    if (predicateNumber != 2) {\n      FAIL_CHECK(\"Instruction \" << mnemonic << \" has \" << predicateNumber\n                                << \" predicate operands.\");\n      continue;\n    }\n\n    if (not consecutivePredicate) {\n      FAIL_CHECK(\"Instruction \"\n                 << mnemonic\n                 << \" doesn't have consecutive predicate operands.\");\n    }\n\n    if (numCCRRegClassID > 1) {\n      FAIL_CHECK(\"Instruction \" << mnemonic << \" has \" << numCCRRegClassID\n                                << \" llvm::ARM::CCRRegClassID operand.\");\n    }\n  }\n}\n\n// LLVM MCInstrDesc specify some operand are TIED_TO another one. We want to\n// verify the followed properties:\n//\n// - If the operation is variadic, the last operand isn't TIED_TO another one.\n// - Two operands can't be TIED_TO to the same operand\n// - An operand is TIED_TO an previous operand\n//\n// An instruction with TIED_TO operand must be in one of theses two cases:\n// - The operand that is tied are the first in the list, and no futher operand.\n//    -> In this case, the instAnalysis skip the tieded operand\n// - Each tied operand is tied to the previous operand.\n//    -> In this case, the instAnalysis merge the operand with the previous one.\n\nvoid LLVMOperandInfoCheck::checkTIEDOperand(QBDI::CPUMode cpuMode) {\n\n  const llvm::MCInstrInfo &MCII = getCPU(cpuMode).getMCII();\n\n  for (unsigned opcode = 0; opcode < llvm::ARM::INSTRUCTION_LIST_END;\n       opcode++) {\n\n    const llvm::MCInstrDesc &desc = MCII.get(opcode);\n    const char *mnemonic = MCII.getName(opcode).data();\n    const int numOperand = desc.getNumOperands();\n\n    // the opcode is a pseudo instruction used by LLVM internally\n    if (desc.isPseudo())\n      continue;\n\n    if (desc.isVariadic()) {\n      if (numOperand == 0) {\n        FAIL_CHECK(\"Instruction \"\n                   << mnemonic << \" is variadic but doesn't have any operand.\");\n      }\n      if (desc.getOperandConstraint(numOperand - 1, llvm::MCOI::TIED_TO) !=\n          -1) {\n        FAIL_CHECK(\n            \"Instruction \"\n            << mnemonic\n            << \" is variadic but the last operand is tied to another one.\");\n      }\n      for (int opn = 0; opn < numOperand; opn++) {\n        if (desc.getOperandConstraint(opn, llvm::MCOI::TIED_TO) ==\n            numOperand - 1) {\n          FAIL_CHECK(\"Instruction \" << mnemonic\n                                    << \" is variadic but the operand \" << opn\n                                    << \"is tied to the variadic operand.\");\n        }\n      }\n    }\n\n    bool tiedtoPreviousOperand = true;\n    int numTied = 0;\n\n    for (int opn = 0; opn < numOperand; opn++) {\n      int opnTied = desc.getOperandConstraint(opn, llvm::MCOI::TIED_TO);\n      if (opnTied != -1) {\n        numTied++;\n        if (opnTied >= opn) {\n          FAIL_CHECK(\"Instruction \" << mnemonic << \" has the operand \" << opn\n                                    << \" tied to the next operand \" << opnTied\n                                    << \".\");\n        }\n        if (opnTied != opn - 1) {\n          tiedtoPreviousOperand = false;\n        }\n\n        for (int opn2 = opn + 1; opn2 < numOperand; opn2++) {\n          int opnTied2 = desc.getOperandConstraint(opn2, llvm::MCOI::TIED_TO);\n          if (opnTied == opnTied2) {\n            FAIL_CHECK(\"Instruction \" << mnemonic << \" has operands \" << opn\n                                      << \" and \" << opn2\n                                      << \" both tied to the same operand\");\n          }\n        }\n      }\n    }\n\n    int sequencTied = 0;\n    for (int opn = 0; opn < numTied; opn++) {\n      bool found = false;\n      for (int op = opn + 1; op < numOperand; op++) {\n        if (desc.getOperandConstraint(op, llvm::MCOI::TIED_TO) == opn) {\n          sequencTied++;\n          found = true;\n          break;\n        }\n      }\n      if (not found) {\n        break;\n      }\n    }\n\n    if ((sequencTied != numTied) and (not tiedtoPreviousOperand)) {\n      FAIL_CHECK(\"Instruction \"\n                 << mnemonic\n                 << \" fail to verify Bias or Previous Operand property.\");\n    }\n  }\n}\n\nTEST_CASE_METHOD(LLVMOperandInfoCheck, \"LLVMOperandInfo-CrossCheck\") {\n  checkPredicate(QBDI::CPUMode::ARM);\n  checkPredicate(QBDI::CPUMode::Thumb);\n  checkTIEDOperand(QBDI::CPUMode::ARM);\n  checkTIEDOperand(QBDI::CPUMode::Thumb);\n}\n"
  },
  {
    "path": "test/Patch/ARM/MemoryAccessTable_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <set>\n\n#include <catch2/catch_test_macros.hpp>\n#include <stdio.h>\n\n#include \"ARMInstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"Patch/InstInfo.h\"\n#include \"Patch/MemoryAccessTable.h\"\n\n#define MAXFAIL 20\n\nnamespace {\n\nusing namespace llvm::ARM;\n\nconst std::set<unsigned> unsupportedInst{\n    // clang-format off\n    // PseudoInst\n    LDRcp,\n    VLD1LNq16Pseudo,\n    VLD1LNq16Pseudo_UPD,\n    VLD1LNq32Pseudo,\n    VLD1LNq32Pseudo_UPD,\n    VLD1LNq8Pseudo,\n    VLD1LNq8Pseudo_UPD,\n    VLD1d16QPseudo,\n    VLD1d16QPseudoWB_fixed,\n    VLD1d16QPseudoWB_register,\n    VLD1d16TPseudo,\n    VLD1d16TPseudoWB_fixed,\n    VLD1d16TPseudoWB_register,\n    VLD1d32QPseudo,\n    VLD1d32QPseudoWB_fixed,\n    VLD1d32QPseudoWB_register,\n    VLD1d32TPseudo,\n    VLD1d32TPseudoWB_fixed,\n    VLD1d32TPseudoWB_register,\n    VLD1d64QPseudo,\n    VLD1d64QPseudoWB_fixed,\n    VLD1d64QPseudoWB_register,\n    VLD1d64TPseudo,\n    VLD1d64TPseudoWB_fixed,\n    VLD1d64TPseudoWB_register,\n    VLD1d8QPseudo,\n    VLD1d8QPseudoWB_fixed,\n    VLD1d8QPseudoWB_register,\n    VLD1d8TPseudo,\n    VLD1d8TPseudoWB_fixed,\n    VLD1d8TPseudoWB_register,\n    VLD1q16HighQPseudo,\n    VLD1q16HighQPseudo_UPD,\n    VLD1q16HighTPseudo,\n    VLD1q16HighTPseudo_UPD,\n    VLD1q16LowQPseudo_UPD,\n    VLD1q16LowTPseudo_UPD,\n    VLD1q32HighQPseudo,\n    VLD1q32HighQPseudo_UPD,\n    VLD1q32HighTPseudo,\n    VLD1q32HighTPseudo_UPD,\n    VLD1q32LowQPseudo_UPD,\n    VLD1q32LowTPseudo_UPD,\n    VLD1q64HighQPseudo,\n    VLD1q64HighQPseudo_UPD,\n    VLD1q64HighTPseudo,\n    VLD1q64HighTPseudo_UPD,\n    VLD1q64LowQPseudo_UPD,\n    VLD1q64LowTPseudo_UPD,\n    VLD1q8HighQPseudo,\n    VLD1q8HighQPseudo_UPD,\n    VLD1q8HighTPseudo,\n    VLD1q8HighTPseudo_UPD,\n    VLD1q8LowQPseudo_UPD,\n    VLD1q8LowTPseudo_UPD,\n    VLD2DUPq16EvenPseudo,\n    VLD2DUPq16OddPseudo,\n    VLD2DUPq16OddPseudoWB_fixed,\n    VLD2DUPq16OddPseudoWB_register,\n    VLD2DUPq32EvenPseudo,\n    VLD2DUPq32OddPseudo,\n    VLD2DUPq32OddPseudoWB_fixed,\n    VLD2DUPq32OddPseudoWB_register,\n    VLD2DUPq8EvenPseudo,\n    VLD2DUPq8OddPseudo,\n    VLD2DUPq8OddPseudoWB_fixed,\n    VLD2DUPq8OddPseudoWB_register,\n    VLD2LNd16Pseudo,\n    VLD2LNd16Pseudo_UPD,\n    VLD2LNd32Pseudo,\n    VLD2LNd32Pseudo_UPD,\n    VLD2LNd8Pseudo,\n    VLD2LNd8Pseudo_UPD,\n    VLD2LNq16Pseudo,\n    VLD2LNq16Pseudo_UPD,\n    VLD2LNq32Pseudo,\n    VLD2LNq32Pseudo_UPD,\n    VLD2q16Pseudo,\n    VLD2q16PseudoWB_fixed,\n    VLD2q16PseudoWB_register,\n    VLD2q32Pseudo,\n    VLD2q32PseudoWB_fixed,\n    VLD2q32PseudoWB_register,\n    VLD2q8Pseudo,\n    VLD2q8PseudoWB_fixed,\n    VLD2q8PseudoWB_register,\n    VLD3DUPd16Pseudo,\n    VLD3DUPd16Pseudo_UPD,\n    VLD3DUPd32Pseudo,\n    VLD3DUPd32Pseudo_UPD,\n    VLD3DUPd8Pseudo,\n    VLD3DUPd8Pseudo_UPD,\n    VLD3DUPq16EvenPseudo,\n    VLD3DUPq16OddPseudo,\n    VLD3DUPq16OddPseudo_UPD,\n    VLD3DUPq32EvenPseudo,\n    VLD3DUPq32OddPseudo,\n    VLD3DUPq32OddPseudo_UPD,\n    VLD3DUPq8EvenPseudo,\n    VLD3DUPq8OddPseudo,\n    VLD3DUPq8OddPseudo_UPD,\n    VLD3LNd16Pseudo,\n    VLD3LNd16Pseudo_UPD,\n    VLD3LNd32Pseudo,\n    VLD3LNd32Pseudo_UPD,\n    VLD3LNd8Pseudo,\n    VLD3LNd8Pseudo_UPD,\n    VLD3LNq16Pseudo,\n    VLD3LNq16Pseudo_UPD,\n    VLD3LNq32Pseudo,\n    VLD3LNq32Pseudo_UPD,\n    VLD3d16Pseudo,\n    VLD3d16Pseudo_UPD,\n    VLD3d32Pseudo,\n    VLD3d32Pseudo_UPD,\n    VLD3d8Pseudo,\n    VLD3d8Pseudo_UPD,\n    VLD3q16Pseudo_UPD,\n    VLD3q16oddPseudo,\n    VLD3q16oddPseudo_UPD,\n    VLD3q32Pseudo_UPD,\n    VLD3q32oddPseudo,\n    VLD3q32oddPseudo_UPD,\n    VLD3q8Pseudo_UPD,\n    VLD3q8oddPseudo,\n    VLD3q8oddPseudo_UPD,\n    VLD4DUPd16Pseudo,\n    VLD4DUPd16Pseudo_UPD,\n    VLD4DUPd32Pseudo,\n    VLD4DUPd32Pseudo_UPD,\n    VLD4DUPd8Pseudo,\n    VLD4DUPd8Pseudo_UPD,\n    VLD4DUPq16EvenPseudo,\n    VLD4DUPq16OddPseudo,\n    VLD4DUPq16OddPseudo_UPD,\n    VLD4DUPq32EvenPseudo,\n    VLD4DUPq32OddPseudo,\n    VLD4DUPq32OddPseudo_UPD,\n    VLD4DUPq8EvenPseudo,\n    VLD4DUPq8OddPseudo,\n    VLD4DUPq8OddPseudo_UPD,\n    VLD4LNd16Pseudo,\n    VLD4LNd16Pseudo_UPD,\n    VLD4LNd32Pseudo,\n    VLD4LNd32Pseudo_UPD,\n    VLD4LNd8Pseudo,\n    VLD4LNd8Pseudo_UPD,\n    VLD4LNq16Pseudo,\n    VLD4LNq16Pseudo_UPD,\n    VLD4LNq32Pseudo,\n    VLD4LNq32Pseudo_UPD,\n    VLD4d16Pseudo,\n    VLD4d16Pseudo_UPD,\n    VLD4d32Pseudo,\n    VLD4d32Pseudo_UPD,\n    VLD4d8Pseudo,\n    VLD4d8Pseudo_UPD,\n    VLD4q16Pseudo_UPD,\n    VLD4q16oddPseudo,\n    VLD4q16oddPseudo_UPD,\n    VLD4q32Pseudo_UPD,\n    VLD4q32oddPseudo,\n    VLD4q32oddPseudo_UPD,\n    VLD4q8Pseudo_UPD,\n    VLD4q8oddPseudo,\n    VLD4q8oddPseudo_UPD,\n    VLDMQIA,\n    VST1LNq16Pseudo,\n    VST1LNq16Pseudo_UPD,\n    VST1LNq32Pseudo,\n    VST1LNq32Pseudo_UPD,\n    VST1LNq8Pseudo,\n    VST1LNq8Pseudo_UPD,\n    VST1d16QPseudo,\n    VST1d16QPseudoWB_fixed,\n    VST1d16QPseudoWB_register,\n    VST1d16TPseudo,\n    VST1d16TPseudoWB_fixed,\n    VST1d16TPseudoWB_register,\n    VST1d32QPseudo,\n    VST1d32QPseudoWB_fixed,\n    VST1d32QPseudoWB_register,\n    VST1d32TPseudo,\n    VST1d32TPseudoWB_fixed,\n    VST1d32TPseudoWB_register,\n    VST1d64QPseudo,\n    VST1d64QPseudoWB_fixed,\n    VST1d64QPseudoWB_register,\n    VST1d64TPseudo,\n    VST1d64TPseudoWB_fixed,\n    VST1d64TPseudoWB_register,\n    VST1d8QPseudo,\n    VST1d8QPseudoWB_fixed,\n    VST1d8QPseudoWB_register,\n    VST1d8TPseudo,\n    VST1d8TPseudoWB_fixed,\n    VST1d8TPseudoWB_register,\n    VST1q16HighQPseudo,\n    VST1q16HighQPseudo_UPD,\n    VST1q16HighTPseudo,\n    VST1q16HighTPseudo_UPD,\n    VST1q16LowQPseudo_UPD,\n    VST1q16LowTPseudo_UPD,\n    VST1q32HighQPseudo,\n    VST1q32HighQPseudo_UPD,\n    VST1q32HighTPseudo,\n    VST1q32HighTPseudo_UPD,\n    VST1q32LowQPseudo_UPD,\n    VST1q32LowTPseudo_UPD,\n    VST1q64HighQPseudo,\n    VST1q64HighQPseudo_UPD,\n    VST1q64HighTPseudo,\n    VST1q64HighTPseudo_UPD,\n    VST1q64LowQPseudo_UPD,\n    VST1q64LowTPseudo_UPD,\n    VST1q8HighQPseudo,\n    VST1q8HighQPseudo_UPD,\n    VST1q8HighTPseudo,\n    VST1q8HighTPseudo_UPD,\n    VST1q8LowQPseudo_UPD,\n    VST1q8LowTPseudo_UPD,\n    VST2LNd16Pseudo,\n    VST2LNd16Pseudo_UPD,\n    VST2LNd32Pseudo,\n    VST2LNd32Pseudo_UPD,\n    VST2LNd8Pseudo,\n    VST2LNd8Pseudo_UPD,\n    VST2LNq16Pseudo,\n    VST2LNq16Pseudo_UPD,\n    VST2LNq32Pseudo,\n    VST2LNq32Pseudo_UPD,\n    VST2q16Pseudo,\n    VST2q16PseudoWB_fixed,\n    VST2q16PseudoWB_register,\n    VST2q32Pseudo,\n    VST2q32PseudoWB_fixed,\n    VST2q32PseudoWB_register,\n    VST2q8Pseudo,\n    VST2q8PseudoWB_fixed,\n    VST2q8PseudoWB_register,\n    VST3LNd16Pseudo,\n    VST3LNd16Pseudo_UPD,\n    VST3LNd32Pseudo,\n    VST3LNd32Pseudo_UPD,\n    VST3LNd8Pseudo,\n    VST3LNd8Pseudo_UPD,\n    VST3LNq16Pseudo,\n    VST3LNq16Pseudo_UPD,\n    VST3LNq32Pseudo,\n    VST3LNq32Pseudo_UPD,\n    VST3d16Pseudo,\n    VST3d16Pseudo_UPD,\n    VST3d32Pseudo,\n    VST3d32Pseudo_UPD,\n    VST3d8Pseudo,\n    VST3d8Pseudo_UPD,\n    VST3q16Pseudo_UPD,\n    VST3q16oddPseudo,\n    VST3q16oddPseudo_UPD,\n    VST3q32Pseudo_UPD,\n    VST3q32oddPseudo,\n    VST3q32oddPseudo_UPD,\n    VST3q8Pseudo_UPD,\n    VST3q8oddPseudo,\n    VST3q8oddPseudo_UPD,\n    VST4LNd16Pseudo,\n    VST4LNd16Pseudo_UPD,\n    VST4LNd32Pseudo,\n    VST4LNd32Pseudo_UPD,\n    VST4LNd8Pseudo,\n    VST4LNd8Pseudo_UPD,\n    VST4LNq16Pseudo,\n    VST4LNq16Pseudo_UPD,\n    VST4LNq32Pseudo,\n    VST4LNq32Pseudo_UPD,\n    VST4d16Pseudo,\n    VST4d16Pseudo_UPD,\n    VST4d32Pseudo,\n    VST4d32Pseudo_UPD,\n    VST4d8Pseudo,\n    VST4d8Pseudo_UPD,\n    VST4q16Pseudo_UPD,\n    VST4q16oddPseudo,\n    VST4q16oddPseudo_UPD,\n    VST4q32Pseudo_UPD,\n    VST4q32oddPseudo,\n    VST4q32oddPseudo_UPD,\n    VST4q8Pseudo_UPD,\n    VST4q8oddPseudo,\n    VST4q8oddPseudo_UPD,\n    VSTMQIA,\n\n    // arm-M instruction\n    VLLDM,\n    VLLDM_T2,\n    VLSTM,\n    VLSTM_T2,\n\n    // Unsupported instruction\n    CDP,\n    CDP2,\n    t2CDP,\n    t2CDP2,\n    // system instruction\n    MCR,\n    MCR2,\n    MCRR,\n    MCRR2,\n    MRC,\n    MRC2,\n    sysLDMDA,\n    sysLDMDA_UPD,\n    sysLDMDB,\n    sysLDMDB_UPD,\n    sysLDMIA,\n    sysLDMIA_UPD,\n    sysLDMIB,\n    sysLDMIB_UPD,\n    sysSTMDA,\n    sysSTMDA_UPD,\n    sysSTMDB,\n    sysSTMDB_UPD,\n    sysSTMIA,\n    sysSTMIA_UPD,\n    sysSTMIB,\n    sysSTMIB_UPD,\n    t2MCR,\n    t2MCR2,\n    t2MCRR,\n    t2MCRR2,\n    t2MRC,\n    t2MRC2,\n    // unknown instruction\n    t__brkdiv0,\n    // clang-format on\n};\n\n// instruction that reads memory but without mayLoad\nconst std::set<unsigned> fixupRead{\n    // clang-format off\n    t2LDRBT,\n    t2LDRT,\n    t2TBB,\n    t2TBH,\n    // clang-format on\n};\n\n// instruction that writes memory but without mayStore\nconst std::set<unsigned> fixupWrite{\n    // clang-format off\n    STRBT_POST_IMM,\n    STRBT_POST_REG,\n    STRH_PRE,\n    t2STRBT,\n    t2STRT,\n    // clang-format on\n};\n\n// instruction with mayLoad but don't reads memory\nconst std::set<unsigned> fixupNoRead{\n    // clang-format off\n    CLREX,\n    DBG,\n    DMB,\n    DSB,\n    HINT,\n    ISB,\n    PLDWi12,\n    PLDWrs,\n    PLDi12,\n    PLDrs,\n    PLIi12,\n    PLIrs,\n    SADD16,\n    SADD8,\n    SASX,\n    SEL,\n    SSAX,\n    SSUB16,\n    SSUB8,\n    STC2L_OFFSET,\n    STC2_OFFSET,\n    STCL_OFFSET,\n    STC_OFFSET,\n    STLEX,\n    STLEXB,\n    STLEXH,\n    STREX,\n    STREXB,\n    STREXH,\n    UADD16,\n    UADD8,\n    UASX,\n    UDF,\n    USAX,\n    USUB16,\n    USUB8,\n    VMRS,\n    VMSR,\n    t2CLREX,\n    t2DBG,\n    t2DMB,\n    t2DSB,\n    t2HINT,\n    t2ISB,\n    t2PLDWi12,\n    t2PLDWi8,\n    t2PLDWs,\n    t2PLDi12,\n    t2PLDi8,\n    t2PLDpci,\n    t2PLDs,\n    t2PLIi12,\n    t2PLIi8,\n    t2PLIpci,\n    t2PLIs,\n    t2SADD16,\n    t2SADD8,\n    t2SASX,\n    t2SEL,\n    t2SSAX,\n    t2SSUB16,\n    t2SSUB8,\n    t2STC2L_OFFSET,\n    t2STC2_OFFSET,\n    t2STCL_OFFSET,\n    t2STC_OFFSET,\n    t2STLEX,\n    t2STLEXB,\n    t2STLEXH,\n    t2STREX,\n    t2STREXB,\n    t2STREXH,\n    t2UADD16,\n    t2UADD8,\n    t2UASX,\n    t2UDF,\n    t2USAX,\n    t2USUB16,\n    t2USUB8,\n    tHINT,\n    tUDF,\n    // clang-format on\n};\n\n// instruction with mayStore but don't writes memory\nconst std::set<unsigned> fixupNoWrite{\n    // clang-format off\n    CLREX,\n    DBG,\n    DMB,\n    DSB,\n    HINT,\n    ISB,\n    LDAEX,\n    LDAEXB,\n    LDAEXH,\n    LDC2L_OFFSET,\n    LDC2_OFFSET,\n    LDCL_OFFSET,\n    LDC_OFFSET,\n    LDREX,\n    LDREXB,\n    LDREXH,\n    PLDWi12,\n    PLDWrs,\n    PLDi12,\n    PLDrs,\n    PLIi12,\n    PLIrs,\n    SADD16,\n    SADD8,\n    SASX,\n    SSAX,\n    SSUB16,\n    SSUB8,\n    UADD16,\n    UADD8,\n    UASX,\n    UDF,\n    USAX,\n    USUB16,\n    USUB8,\n    VMRS,\n    VMSR,\n    t2CLREX,\n    t2DBG,\n    t2DMB,\n    t2DSB,\n    t2HINT,\n    t2ISB,\n    t2LDAEX,\n    t2LDAEXB,\n    t2LDAEXH,\n    t2LDC2L_OFFSET,\n    t2LDC2_OFFSET,\n    t2LDCL_OFFSET,\n    t2LDC_OFFSET,\n    t2LDREX,\n    t2LDREXB,\n    t2LDREXH,\n    t2PLDWi12,\n    t2PLDWi8,\n    t2PLDWs,\n    t2PLDi12,\n    t2PLDi8,\n    t2PLDpci,\n    t2PLDs,\n    t2PLIi12,\n    t2PLIi8,\n    t2PLIpci,\n    t2PLIs,\n    t2SADD16,\n    t2SADD8,\n    t2SASX,\n    t2SSAX,\n    t2SSUB16,\n    t2SSUB8,\n    t2UADD16,\n    t2UADD8,\n    t2UASX,\n    t2UDF,\n    t2USAX,\n    t2USUB16,\n    t2USUB8,\n    tHINT,\n    tUDF,\n    // clang-format on\n};\n\n} // namespace\n\nTEST_CASE_METHOD(MemoryAccessTable, \"MemoryAccessTable-CrossCheck\") {\n\n  const QBDI::LLVMCPU &llvmcpu = getCPU(QBDI::CPUMode::DEFAULT);\n  const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n  int nbfail = 0;\n\n  for (unsigned opcode = 0; opcode < llvm::ARM::INSTRUCTION_LIST_END;\n       opcode++) {\n    if (unsupportedInst.count(opcode) == 1)\n      continue;\n\n    const llvm::MCInstrDesc &desc = MCII.get(opcode);\n    const char *mnemonic = MCII.getName(opcode).data();\n\n    // InstInfo_ARM.cpp only use inst->getOpcode(). The MCInst doesn't need\n    // to have his operand\n    llvm::MCInst inst;\n    inst.setOpcode(opcode);\n\n    bool doRead =\n        (QBDI::getReadSize(inst, llvmcpu) != 0) || QBDI::unsupportedRead(inst);\n    bool doWrite = (QBDI::getWriteSize(inst, llvmcpu) != 0) ||\n                   QBDI::unsupportedWrite(inst);\n    bool mayRead = desc.mayLoad();\n    bool mayWrite = desc.mayStore();\n\n    bool bypassRead = false;\n    bool bypassWrite = false;\n\n    // the opcode is a pseudo instruction used by LLVM internally\n    if (desc.isPseudo()) {\n      if (doRead or doWrite) {\n        WARN(\"Pseudo instruction \" << mnemonic << \" in InstInfo\");\n      }\n      continue;\n    }\n\n    // llvm mayLoad and mayStore fixup\n    if (fixupRead.count(opcode) == 1) {\n      if (doRead && !mayRead)\n        bypassRead = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupRead\");\n    }\n\n    if (fixupNoRead.count(opcode) == 1) {\n      if (!doRead && mayRead)\n        bypassRead = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupNoRead\");\n    }\n\n    if (fixupWrite.count(opcode) == 1) {\n      if (doWrite && !mayWrite)\n        bypassWrite = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupWrite\");\n    }\n\n    if (fixupNoWrite.count(opcode) == 1) {\n      if (!doWrite && mayWrite)\n        bypassWrite = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupNoWrite\");\n    }\n\n    if (!bypassRead && doRead != mayRead) {\n      if (doRead && !mayRead) {\n        FAIL_CHECK(\"Unexpected read for \" << mnemonic);\n        nbfail++;\n      } else if (!doRead && mayRead) {\n        FAIL_CHECK(\"Missing read for \" << mnemonic);\n        nbfail++;\n      }\n    }\n\n    if (!bypassWrite && doWrite != mayWrite) {\n      if (doWrite && !mayWrite) {\n        FAIL_CHECK(\"Unexpected write for \" << mnemonic);\n        nbfail++;\n      } else if (!doWrite && mayWrite) {\n        FAIL_CHECK(\"Missing write for \" << mnemonic);\n        nbfail++;\n      }\n    }\n    if (nbfail >= MAXFAIL) {\n      FAIL(\"Too many fails, abort MemoryAccessTable validation\");\n    }\n  }\n}\n"
  },
  {
    "path": "test/Patch/ARM/Patch_Test_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"Patch/Patch_Test.h\"\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STLDMIA\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(STLDMIA_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STLDMIB\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(STLDMIB_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STLDMDA\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(STLDMDA_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STLDMDB\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(STLDMDB_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STMDB_LDMIA_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  comparedExec(STMDB_LDMIA_post_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STMDA_LDMIB_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  comparedExec(STMDA_LDMIB_post_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STMIB_LDMDA_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  comparedExec(STMIB_LDMDA_post_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-STMIA_LDMDB_post\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  comparedExec(STMIA_LDMDB_post_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-LDREXTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n  uint8_t buffer[4096] = {0};\n\n  initContext(inputState);\n  inputState.gprState.r11 = (QBDI::rword)&buffer;\n  comparedExec(LDREXTest_s, inputState, 4096);\n}\n"
  },
  {
    "path": "test/Patch/ARM/Patch_Test_Thumb.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Patch/ARM/Patch_Test_Thumb.h\"\n#include <catch2/catch_test_macros.hpp>\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tGPRSave\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tGPRSave_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tGPRShuffle\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tGPRShuffle_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tRelativeAddressing\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tRelativeAddressing_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tFibonacciRecursion\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  inputState.gprState.r0 = (rand() % 20) + 2;\n  comparedExec(tFibonacciRecursion_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tBranchCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tBranchCondTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tBranchLinkCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tBranchLinkCondTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tBranchRegisterCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tBranchRegisterCondTest_s, inputState, 4096);\n\n  CHECK(inputState.gprState.r10 == 0);\n  CHECK(inputState.gprState.r11 == 0);\n  CHECK(inputState.gprState.r12 == 0);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tPushPopTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tPushPopTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdmiaStmdbWbackTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdmiaStmdbWbackTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdmdbStmiaWbackTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdmdbStmiaWbackTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdmiaStmdbTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdmiaStmdbTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdmdbStmiaTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdmdbStmiaTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdrPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdrPCTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdrbPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdrbPCTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdrdPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdrdPCTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdrhPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdrhPCTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdrsbPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdrsbPCTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tLdrshPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tLdrshPCTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tMovPCTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tMovPCTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tTBBTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tTBBTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tTBHTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tTBHTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tITCondTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(tITCondTest_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test_Thumb, \"Patch_Test-tldrexTest\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n  uint8_t buffer[4096] = {0};\n\n  initContext(inputState);\n  inputState.gprState.r11 = (QBDI::rword)&buffer;\n  comparedExec(tldrexTest_s, inputState, 4096);\n}\n"
  },
  {
    "path": "test/Patch/ARM/Patch_Test_Thumb.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCH_TEST_THUMB_H\n#define PATCH_TEST_THUMB_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"QBDI/Platform.h\"\n#include \"Patch/Utils.h\"\n\n#include \"ComparedExecutor_Thumb.h\"\n\nclass Patch_Test_Thumb : public ComparedExecutor_Thumb {};\n\n#endif\n"
  },
  {
    "path": "test/Patch/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/Utils.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Instr_Test.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Patch_Test.cpp\")\n\nif(QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "test/Patch/Instr_Test.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Patch/Instr_Test.h\"\n#include <catch2/catch_test_macros.hpp>\n\nQBDI::VMAction increment(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                         QBDI::FPRState *fprState, void *data) {\n  *((uint64_t *)data) = *((uint64_t *)data) + 1;\n  return QBDI::VMAction::CONTINUE;\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-GPRSave_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(GPRSave_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-GPRShuffle_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(GPRShuffle_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-RelativeAddressing_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(RelativeAddressing_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-ConditionalBranching_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(ConditionalBranching_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-FibonacciRecursion_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n  QBDI_GPR_SET(&inputState.gprState, 0, (rand() % 20) + 2);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(FibonacciRecursion_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-StackTricks_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n  QBDI_GPR_SET(&inputState.gprState, 0, (rand() % 20) + 2);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(StackTricks_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\");\n}\n"
  },
  {
    "path": "test/Patch/Instr_Test.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTR_TEST_H\n#define INSTR_TEST_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"QBDI/Platform.h\"\n#include \"Patch/Utils.h\"\n\n#if defined(QBDI_ARCH_X86)\n#include \"X86/ComparedExecutor_X86.h\"\n\nclass Instr_Test : public ComparedExecutor_X86 {};\n#elif defined(QBDI_ARCH_X86_64)\n#include \"X86_64/ComparedExecutor_X86_64.h\"\n\nclass Instr_Test : public ComparedExecutor_X86_64 {};\n#elif defined(QBDI_ARCH_ARM)\n#include \"ARM/ComparedExecutor_ARM.h\"\n\nclass Instr_Test : public ComparedExecutor_ARM {};\n\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"AARCH64/ComparedExecutor_AARCH64.h\"\n\nclass Instr_Test : public ComparedExecutor_AARCH64 {};\n\n#else\n#error \"Architecture not supported\"\n#endif\n\nQBDI::VMAction increment(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                         QBDI::FPRState *fprState, void *data);\n\n#endif\n"
  },
  {
    "path": "test/Patch/MemoryAccessTable.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDITEST_MEMORYACCESSTABLE\n#define QBDITEST_MEMORYACCESSTABLE\n\n#include \"TestSetup/LLVMTestEnv.h\"\n\nclass MemoryAccessTable : public LLVMTestEnv {};\n\n#endif /* QBDITEST_MEMORYACCESSTABLE */\n"
  },
  {
    "path": "test/Patch/Patch_Test.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"Patch/Patch_Test.h\"\n#include <catch2/catch_test_macros.hpp>\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-EmptyFunction\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(\"\", inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-GPRSave\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(GPRSave_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-GPRShuffle\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(GPRShuffle_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-RelativeAddressing\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(RelativeAddressing_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-ConditionalBranching\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(ConditionalBranching_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-FibonacciRecursion\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  QBDI_GPR_SET(&inputState.gprState, 0, (rand() % 20) + 2);\n  comparedExec(FibonacciRecursion_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-StackTricks\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  QBDI_GPR_SET(&inputState.gprState, 0, (rand() % 20) + 2);\n  comparedExec(StackTricks_s, inputState, 4096);\n}\n"
  },
  {
    "path": "test/Patch/Patch_Test.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PATCH_TEST_H\n#define PATCH_TEST_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"QBDI/Platform.h\"\n#include \"Patch/Utils.h\"\n\n#if defined(QBDI_ARCH_X86)\n#include \"X86/ComparedExecutor_X86.h\"\n\nclass Patch_Test : public ComparedExecutor_X86 {};\n#elif defined(QBDI_ARCH_X86_64)\n#include \"X86_64/ComparedExecutor_X86_64.h\"\n\nclass Patch_Test : public ComparedExecutor_X86_64 {};\n#elif defined(QBDI_ARCH_ARM)\n#include \"ARM/ComparedExecutor_ARM.h\"\n\nclass Patch_Test : public ComparedExecutor_ARM {};\n\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"AARCH64/ComparedExecutor_AARCH64.h\"\n\nclass Patch_Test : public ComparedExecutor_AARCH64 {};\n\n#else\n#error \"Architecture not supported\"\n#endif\n\n#endif\n"
  },
  {
    "path": "test/Patch/Utils.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdlib.h>\n\n#include \"Utils.h\"\n\nQBDI::rword seed_random() {\n\n  unsigned int seed;\n  if (getenv(\"TEST_SEED\")) {\n    seed = atoi(getenv(\"TEST_SEED\"));\n  } else {\n    seed = rand();\n  }\n  srand(seed);\n  return seed;\n}\n\nQBDI::rword get_random() {\n  if constexpr (QBDI::it_bits_32) {\n    return rand();\n  } else {\n    return ((uint64_t)rand()) << 32 || rand();\n  }\n}\n"
  },
  {
    "path": "test/Patch/Utils.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef UTILS_H\n#define UTILS_H\n\n#include \"QBDI/State.h\"\n\nQBDI::rword get_random();\nQBDI::rword seed_random();\n\n#endif /* UTILS_H */\n"
  },
  {
    "path": "test/Patch/X86/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/ComparedExecutor_X86.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/../X86_64/MemoryAccessTable_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Instr_Test_X86.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Patch_Test_X86.cpp\")\n\nif(QBDI_PLATFORM_WINDOWS)\n  target_sources(QBDITest\n                 PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/WIN86_RunRealExec.asm\")\n  set_source_files_properties(\"${CMAKE_CURRENT_LIST_DIR}/WIN86_RunRealExec.asm\"\n                              PROPERTIES COMPILE_FLAGS \"/safeseh\")\nendif()\n"
  },
  {
    "path": "test/Patch/X86/ComparedExecutor_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Veesion 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"ComparedExecutor_X86.h\"\n#include \"Patch/Utils.h\"\n\n#if defined(_M_IX86)\n\nextern \"C\" void runRealExec(const uint8_t *code, void *ctxBlock);\n\n#endif\n\nInMemoryObject\nComparedExecutor_X86::compileWithContextSwitch(const char *source) {\n  std::ostringstream finalSource;\n\n  // Assemble the sources\n  finalSource\n      << \"mov \" << offsetof(QBDI::Context, gprState.eflags) << \"(%edi), %eax\\n\"\n      << \"push %eax\\n\"\n      << \"popf\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.eax) << \"(%edi), %eax\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.ebx) << \"(%edi), %ebx\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.ecx) << \"(%edi), %ecx\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.edx) << \"(%edi), %edx\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.esi) << \"(%edi), %esi\\n\"\n      << \"mov %esp, \" << offsetof(QBDI::Context, hostState.sp) << \"(%edi)\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.ebp) << \"(%edi), %ebp\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.esp) << \"(%edi), %esp\\n\"\n      << \"push %edi\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.edi) << \"(%edi), %edi\\n\";\n  finalSource << source;\n  finalSource\n      << \"pop %esp\\n\"\n      << \"mov %eax, \" << offsetof(QBDI::Context, gprState.eax) << \"(%esp)\\n\"\n      << \"mov %ebx, \" << offsetof(QBDI::Context, gprState.ebx) << \"(%esp)\\n\"\n      << \"mov %ecx, \" << offsetof(QBDI::Context, gprState.ecx) << \"(%esp)\\n\"\n      << \"mov %edx, \" << offsetof(QBDI::Context, gprState.edx) << \"(%esp)\\n\"\n      << \"mov %esi, \" << offsetof(QBDI::Context, gprState.esi) << \"(%esp)\\n\"\n      << \"mov %edi, \" << offsetof(QBDI::Context, gprState.edi) << \"(%esp)\\n\"\n      << \"mov %ebp, \" << offsetof(QBDI::Context, gprState.ebp) << \"(%esp)\\n\"\n      << \"mov %esp, %edi\\n\"\n      << \"mov \" << offsetof(QBDI::Context, hostState.sp) << \"(%esp), %esp\\n\"\n      << \"pushf\\n\"\n      << \"pop %eax\\n\"\n      << \"mov %eax, \" << offsetof(QBDI::Context, gprState.eflags) << \"(%edi)\\n\"\n      << \"ret\\n\";\n\n  return InMemoryObject(finalSource.str().c_str());\n}\n\nQBDI::Context ComparedExecutor_X86::jitExec(llvm::ArrayRef<uint8_t> code,\n                                            QBDI::Context &inputState,\n                                            llvm::sys::MemoryBlock &stack) {\n  QBDI::Context outputState;\n  QBDI::Context outerState;\n  llvm::sys::MemoryBlock ctxBlock;\n  llvm::sys::MemoryBlock outerStack;\n  std::error_code ec;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  outerStack = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  memset((void *)&outerState, 0, sizeof(QBDI::Context));\n  // Put the inputState on the stack\n  inputState.gprState.ebp = (QBDI::rword)stack.base() + stack.allocatedSize();\n  inputState.gprState.esp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  memcpy((void *)ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Prepare the outerState to fake the realExec() action\n  outerState.gprState.ebp =\n      (QBDI::rword)outerStack.base() + outerStack.allocatedSize();\n  outerState.gprState.esp = (QBDI::rword)outerStack.base() +\n                            outerStack.allocatedSize() - sizeof(QBDI::rword);\n  *((QBDI::rword *)outerState.gprState.esp) = (QBDI::rword)0;\n  outerState.gprState.edi = (QBDI::rword)ctxBlock.base();\n\n  vm.setGPRState(&outerState.gprState);\n  vm.setFPRState(&outerState.fprState);\n  vm.addInstrumentedRange((QBDI::rword)code.data(),\n                          (QBDI::rword)code.data() + code.size());\n  vm.run((QBDI::rword)code.data(), 0);\n  vm.removeInstrumentedRange((QBDI::rword)code.data(),\n                             (QBDI::rword)code.data() + code.size());\n\n  memcpy((void *)&outputState, (void *)ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n  llvm::sys::Memory::releaseMappedMemory(outerStack);\n\n  return outputState;\n}\n\nQBDI::Context ComparedExecutor_X86::realExec(llvm::ArrayRef<uint8_t> code,\n                                             QBDI::Context &inputState,\n                                             llvm::sys::MemoryBlock &stack) {\n\n  QBDI::Context outputState;\n  std::error_code ec;\n  llvm::sys::MemoryBlock ctxBlock;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n\n  // Put the inputState on the stack\n  QBDI_GPR_SET(&inputState.gprState, QBDI::REG_BP,\n               (QBDI::rword)stack.base() + stack.allocatedSize());\n  QBDI_GPR_SET(&inputState.gprState, QBDI::REG_SP,\n               (QBDI::rword)stack.base() + stack.allocatedSize());\n\n  // Copy the input context\n  memcpy(ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n// Execute\n#if defined(_M_IX86)\n  runRealExec(code.data(), ctxBlock.base());\n#else\n  __asm__ volatile(\n      \"mov %1, %%edi;\"\n      \"mov %0, %%esi;\"\n      \"push %%ebp;\"\n      \"call *%%esi;\"\n      \"pop %%ebp;\"\n      :\n      : \"rm\"(code.data()), \"rm\"(ctxBlock.base())\n      : \"eax\", \"ebx\", \"ecx\", \"edx\", \"edi\", \"esi\");\n#endif\n  // Get the output context\n  memcpy((void *)&outputState, ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n\n  return outputState;\n}\n\nvoid ComparedExecutor_X86::initContext(QBDI::Context &ctx) {\n  memset(&ctx, 0, sizeof(QBDI::Context));\n  ctx.gprState.eax = get_random();\n  ctx.gprState.ebx = get_random();\n  ctx.gprState.ecx = get_random();\n  ctx.gprState.edx = get_random();\n  ctx.gprState.esi = get_random();\n  ctx.gprState.edi = get_random();\n}\n\nconst char *GPRSave_s =\n    \"    mov $0x1, %eax\\n\"\n    \"    mov $0x2, %ebx\\n\"\n    \"    mov $0x3, %ecx\\n\"\n    \"    mov $0x4, %edx\\n\"\n    \"    mov $0x5, %esi\\n\"\n    \"    mov $0x6, %edi\\n\";\n\nconst char *GPRShuffle_s =\n    \"    pushal\\n\"\n    \"    popal\\n\"\n    \"    push %eax\\n\"\n    \"    push %ebx\\n\"\n    \"    push %ecx\\n\"\n    \"    push %edx\\n\"\n    \"    push %esi\\n\"\n    \"    push %edi\\n\"\n    \"    pop %esi\\n\"\n    \"    pop %edx\\n\"\n    \"    pop %ecx\\n\"\n    \"    pop %ebx\\n\"\n    \"    pop %eax\\n\"\n    \"    pop %edi\\n\";\n\nconst char *RelativeAddressing_s =\n    \"jmp start\\n\"\n    \"c1:\\n\"\n    \"    .long 0x12345678\\n\"\n    \"start:\\n\"\n    \"    push %ebp\\n\"\n    \"    call L1\\n\"\n    \"L1:\\n\"\n    \"    pop %ebp\\n\"\n    \"    lea c1-L1(%ebp), %esi\\n\"\n    \"    mov c1-L1(%ebp), %ecx\\n\"\n    \"    xor %ecx, %eax\\n\"\n    \"    xor %esi, %esi\\n\"\n    \"    lea c2-L1(%ebp), %edi\\n\"\n    \"    mov c2-L1(%ebp), %edx\\n\"\n    \"    xor %edx, %ebx\\n\"\n    \"    xor %edi, %ebx\\n\"\n    \"    jmp end\\n\"\n    \"c2:\\n\"\n    \"    .long 0x0fedcba9\\n\"\n    \"end:\\n\"\n    \"    pop %ebp\\n\"\n    \"    movl $0x666, -4(%esp)\\n\";\n\nconst char *ConditionalBranching_s =\n    \"    push %edx\\n\"\n    \"    push %ecx\\n\"\n    \"    push %ebx\\n\"\n    \"    push %eax\\n\"\n    \"    call L1\\n\"\n    \"L1:\\n\"\n    \"    pop %ebx\\n\"\n    \"    xor %ecx, %ecx\\n\"\n    \"    xor %edx, %edx\\n\"\n    \"loop:\\n\"\n    \"    movzx (%esp), %ax\\n\"\n    \"    inc %esp\\n\"\n    \"    xor %al, %dl\\n\"\n    \"    ror $12, %edx\\n\"\n    \"    inc %ecx\\n\"\n    \"    cmpl $16, %ecx\\n\"\n    \"    jb loop\\n\"\n    \"    lea checksum-L1(%ebx), %esi\\n\"\n    \"    mov (%esi), %edi\\n\"\n    \"    cmp %edx, %edi\\n\"\n    \"    jne bad\\n\"\n    \"    mov $1, %eax\\n\"\n    \"    jmp end\\n\"\n    \"bad:\\n\"\n    \"    mov $0, %eax\\n\"\n    \"    jmp end\\n\"\n    \"checksum:\\n\"\n    \"    .long 0x32253676\\n\"\n    \"end:\\n\";\n\nconst char *FibonacciRecursion_s =\n    \"   call L1\\n\"\n    \"L1:\\n\"\n    \"   pop %esi\\n\"\n    \"   lea fibo-L1(%esi), %ebx\\n\"\n    \"   push %ebx\\n\"\n    \"   call *0x0(%esp)\\n\"\n    \"   jmp end\\n\"\n    \"fibo:\\n\"\n    \"   sub $0x8, %esp\\n\"\n    \"   cmp $2, %eax\\n\"\n    \"   ja fibo1\\n\"\n    \"   mov $1, %eax\\n\"\n    \"   jmp fibo2\\n\"\n    \"fibo1:\\n\"\n    \"   dec %eax\\n\"\n    \"   mov %eax, 0x4(%esp)\\n\"\n    \"   call fibo\\n\"\n    \"   mov %eax, 0x0(%esp)\\n\"\n    \"   mov 0x4(%esp), %eax\\n\"\n    \"   dec %eax\\n\"\n    \"   lea fibo-L1(%esi), %ebx\\n\"\n    \"   call *%ebx\\n\"\n    \"   add 0x0(%esp), %eax\\n\"\n    \"fibo2:\\n\"\n    \"   add $0x8, %esp\\n\"\n    \"   ret\\n\"\n    \"end:\\n\"\n    \"   pop %ebx\\n\";\n\nconst char *StackTricks_s =\n    \"   push %ebp\\n\"\n    \"   call L1\\n\"\n    \"L1:\\n\"\n    \"   pop %ebp\\n\"\n    \"   lea end-L1(%ebp), %ecx\\n\"\n    \"   lea f1-L1(%ebp), %edx\\n\"\n    \"   push %eax\\n\"\n    \"   push %ecx\\n\"\n    \"   push %edx\\n\"\n    \"   ret\\n\"\n    \"f1:\\n\"\n    \"   mov 0x4(%esp), %eax\\n\"\n    \"   lea f2-L1(%ebp), %ecx\\n\"\n    \"   lea f6-L1(%ebp), %edx\\n\"\n    \"   mov $1, %esi\\n\"\n    \"   cmp $2, %eax\\n\"\n    \"   cmova %ecx, %edx\\n\"\n    \"   push %edx\\n\"\n    \"   ret\\n\"\n    \"f2:\\n\"\n    \"   dec %eax\\n\"\n    \"   lea f4-L1(%ebp), %ecx\\n\"\n    \"   lea f1-L1(%ebp), %edx\\n\"\n    \"   push %eax\\n\"\n    \"   push %ecx\\n\"\n    \"   push %edx\\n\"\n    \"   ret\\n\"\n    \"f4:\\n\"\n    \"   add %esi, %ebx\\n\"\n    \"   dec %eax\\n\"\n    \"   lea f5-L1(%ebp), %ecx\\n\"\n    \"   lea f1-L1(%ebp), %edx\\n\"\n    \"   push %eax\\n\"\n    \"   push %ecx\\n\"\n    \"   push %edx\\n\"\n    \"   ret\\n\"\n    \"f5:\\n\"\n    \"   add %ebx, %esi\\n\"\n    \"f6:\\n\"\n    \"   mov 0x4(%esp), %eax\\n\"\n    \"   ret $0x4\\n\"\n    \"end:\\n\"\n    \"   pop %ebp\\n\"\n    \"   movl $0x666, -4(%esp)\\n\";\n\nconst char *LoopCode_s =\n    \"    push %ecx\\n\"\n    \"    mov $0x0, %eax\\n\"\n    \"    mov $0x5, %ecx\\n\"\n    \"    jecxz loop_start\\n\"\n    \"    mov $0x10, %eax\\n\"\n    \"loop_start:\\n\"\n    \"    addl $0x1, %eax\\n\"\n    \"    loop loop_start\\n\"\n    \"    jecxz loop_end1\\n\"\n    \"    shll\t$0x2, %eax\\n\"\n    \"loop_end1:\\n\"\n    \"    pop %ecx\\n\";\n"
  },
  {
    "path": "test/Patch/X86/ComparedExecutor_X86.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef COMPAREDEXECUTOR_X86_H\n#define COMPAREDEXECUTOR_X86_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n#include \"TestSetup/ShellcodeTester.h\"\n\nextern const char *GPRSave_s;\nextern const char *GPRShuffle_s;\nextern const char *RelativeAddressing_s;\nextern const char *ConditionalBranching_s;\nextern const char *FibonacciRecursion_s;\nextern const char *StackTricks_s;\nextern const char *LoopCode_s;\n\nclass ComparedExecutor_X86 : public ShellcodeTester {\n\npublic:\n  QBDI::Context jitExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                        llvm::sys::MemoryBlock &stack);\n\n  QBDI::Context realExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                         llvm::sys::MemoryBlock &stack);\n\n  InMemoryObject compileWithContextSwitch(const char *source);\n\n  void initContext(QBDI::Context &ctx);\n};\n\n#endif\n"
  },
  {
    "path": "test/Patch/X86/Instr_Test_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"Patch/Instr_Test.h\"\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-LoopCode_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(LoopCode_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n"
  },
  {
    "path": "test/Patch/X86/Patch_Test_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"Patch/Patch_Test.h\"\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-LoopCode\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(LoopCode_s, inputState, 4096);\n}\n"
  },
  {
    "path": "test/Patch/X86/WIN86_RunRealExec.asm",
    "content": ";\n; This file is part of QBDI.\n;\n; Copyright 2017 - 2025 Quarkslab\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n;\n\n.386\n.model flat, C\n\n_TEXT segment\n\nPUBLIC runRealExec\n\n.CODE\n\nrunRealExec PROC\n    push ebp;\n    mov ebp, esp;\n    pushad;\n    mov eax, [ebp+8];\n    mov edi, [ebp+12];\n    call eax;\n    popad;\n    mov esp, ebp;\n    pop ebp;\n    ret;\nrunRealExec ENDP\n\nEND\n\n"
  },
  {
    "path": "test/Patch/X86_64/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest\n  PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/ComparedExecutor_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/MemoryAccessTable_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/LLVMOperandInfo_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Instr_Test_X86_64.cpp\"\n          \"${CMAKE_CURRENT_LIST_DIR}/Patch_Test_X86_64.cpp\")\n\nif(QBDI_PLATFORM_WINDOWS)\n  target_sources(QBDITest\n                 PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/WIN64_RunRealExec.asm\")\nendif()\n"
  },
  {
    "path": "test/Patch/X86_64/ComparedExecutor_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"ComparedExecutor_X86_64.h\"\n#include \"Patch/Utils.h\"\n\n#if defined(_M_X64)\n\nextern \"C\" void runRealExec(const uint8_t *code, void *ctxBlock);\n\n#endif\n\nInMemoryObject\nComparedExecutor_X86_64::compileWithContextSwitch(const char *source) {\n  std::ostringstream finalSource;\n\n  // Assemble the sources\n  finalSource\n      << \"mov \" << offsetof(QBDI::Context, gprState.eflags) << \"(%rdi), %rax\\n\"\n      << \"push %rax\\n\"\n      << \"popfq\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rax) << \"(%rdi), %rax\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rbx) << \"(%rdi), %rbx\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rcx) << \"(%rdi), %rcx\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rdx) << \"(%rdi), %rdx\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rsi) << \"(%rdi), %rsi\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r8) << \"(%rdi), %r8\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r9) << \"(%rdi), %r9\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r10) << \"(%rdi), %r10\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r11) << \"(%rdi), %r11\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r12) << \"(%rdi), %r12\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r13) << \"(%rdi), %r13\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r14) << \"(%rdi), %r14\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.r15) << \"(%rdi), %r15\\n\"\n      << \"mov %rsp, \" << offsetof(QBDI::Context, hostState.sp) << \"(%rdi)\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rbp) << \"(%rdi), %rbp\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rsp) << \"(%rdi), %rsp\\n\"\n      << \"push %rdi\\n\"\n      << \"mov \" << offsetof(QBDI::Context, gprState.rdi) << \"(%rdi), %rdi\\n\";\n  finalSource << source;\n  finalSource\n      << \"pop %rsp\\n\"\n      << \"mov %rax, \" << offsetof(QBDI::Context, gprState.rax) << \"(%rsp)\\n\"\n      << \"mov %rbx, \" << offsetof(QBDI::Context, gprState.rbx) << \"(%rsp)\\n\"\n      << \"mov %rcx, \" << offsetof(QBDI::Context, gprState.rcx) << \"(%rsp)\\n\"\n      << \"mov %rdx, \" << offsetof(QBDI::Context, gprState.rdx) << \"(%rsp)\\n\"\n      << \"mov %rsi, \" << offsetof(QBDI::Context, gprState.rsi) << \"(%rsp)\\n\"\n      << \"mov %rdi, \" << offsetof(QBDI::Context, gprState.rdi) << \"(%rsp)\\n\"\n      << \"mov %r8, \" << offsetof(QBDI::Context, gprState.r8) << \"(%rsp)\\n\"\n      << \"mov %r9, \" << offsetof(QBDI::Context, gprState.r9) << \"(%rsp)\\n\"\n      << \"mov %r10, \" << offsetof(QBDI::Context, gprState.r10) << \"(%rsp)\\n\"\n      << \"mov %r11, \" << offsetof(QBDI::Context, gprState.r11) << \"(%rsp)\\n\"\n      << \"mov %r12, \" << offsetof(QBDI::Context, gprState.r12) << \"(%rsp)\\n\"\n      << \"mov %r13, \" << offsetof(QBDI::Context, gprState.r13) << \"(%rsp)\\n\"\n      << \"mov %r14, \" << offsetof(QBDI::Context, gprState.r14) << \"(%rsp)\\n\"\n      << \"mov %r15, \" << offsetof(QBDI::Context, gprState.r15) << \"(%rsp)\\n\"\n      << \"mov %rbp, \" << offsetof(QBDI::Context, gprState.rbp) << \"(%rsp)\\n\"\n      << \"mov %rsp, %rdi\\n\"\n      << \"mov \" << offsetof(QBDI::Context, hostState.sp) << \"(%rsp), %rsp\\n\"\n      << \"pushfq\\n\"\n      << \"pop %rax\\n\"\n      << \"mov %rax, \" << offsetof(QBDI::Context, gprState.eflags) << \"(%rdi)\\n\"\n      << \"ret\\n\";\n\n  return InMemoryObject(finalSource.str().c_str());\n}\n\nQBDI::Context ComparedExecutor_X86_64::jitExec(llvm::ArrayRef<uint8_t> code,\n                                               QBDI::Context &inputState,\n                                               llvm::sys::MemoryBlock &stack) {\n  QBDI::Context outputState;\n  QBDI::Context outerState;\n  llvm::sys::MemoryBlock ctxBlock;\n  llvm::sys::MemoryBlock outerStack;\n  std::error_code ec;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  outerStack = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  memset((void *)&outerState, 0, sizeof(QBDI::Context));\n  // Put the inputState on the stack\n  inputState.gprState.rbp = (QBDI::rword)stack.base() + stack.allocatedSize();\n  inputState.gprState.rsp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  memcpy((void *)ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n  // Prepare the outerState to fake the realExec() action\n  outerState.gprState.rbp =\n      (QBDI::rword)outerStack.base() + outerStack.allocatedSize();\n  outerState.gprState.rsp = (QBDI::rword)outerStack.base() +\n                            outerStack.allocatedSize() - sizeof(QBDI::rword);\n  *((QBDI::rword *)outerState.gprState.rsp) = (QBDI::rword)0;\n  outerState.gprState.rdi = (QBDI::rword)ctxBlock.base();\n\n  vm.setGPRState(&outerState.gprState);\n  vm.setFPRState(&outerState.fprState);\n  vm.addInstrumentedRange((QBDI::rword)code.data(),\n                          (QBDI::rword)code.data() + code.size());\n  vm.run((QBDI::rword)code.data(), 0);\n  vm.removeInstrumentedRange((QBDI::rword)code.data(),\n                             (QBDI::rword)code.data() + code.size());\n\n  memcpy((void *)&outputState, (void *)ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n  llvm::sys::Memory::releaseMappedMemory(outerStack);\n\n  return outputState;\n}\n\nQBDI::Context ComparedExecutor_X86_64::realExec(llvm::ArrayRef<uint8_t> code,\n                                                QBDI::Context &inputState,\n                                                llvm::sys::MemoryBlock &stack) {\n\n  QBDI::Context outputState;\n  std::error_code ec;\n  llvm::sys::MemoryBlock ctxBlock;\n\n  ctxBlock = llvm::sys::Memory::allocateMappedMemory(\n      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n\n  // Put the inputState on the stack\n  inputState.gprState.rbp = (QBDI::rword)stack.base() + stack.allocatedSize();\n  inputState.gprState.rsp = (QBDI::rword)stack.base() + stack.allocatedSize();\n\n  // Copy the input context\n  memcpy(ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));\n// Execute\n#if defined(_M_X64)\n  runRealExec(code.data(), ctxBlock.base());\n#else\n  __asm__ volatile(\n      \"mov %1, %%rdi;\"\n      \"mov %0, %%rsi;\"\n      \"push %%rbp;\"\n      \"call *%%rsi;\"\n      \"pop %%rbp;\"\n      :\n      : \"rm\"(code.data()), \"rm\"(ctxBlock.base())\n      : \"rax\", \"rbx\", \"rcx\", \"rdx\", \"rdi\", \"rsi\", \"r8\", \"r9\", \"r10\", \"r11\",\n        \"r12\", \"r13\", \"r14\", \"r15\");\n#endif\n  // Get the output context\n  memcpy((void *)&outputState, ctxBlock.base(), sizeof(QBDI::Context));\n\n  llvm::sys::Memory::releaseMappedMemory(ctxBlock);\n\n  return outputState;\n}\n\nvoid ComparedExecutor_X86_64::initContext(QBDI::Context &ctx) {\n  memset(&ctx, 0, sizeof(QBDI::Context));\n  ctx.gprState.rax = get_random();\n  ctx.gprState.rbx = get_random();\n  ctx.gprState.rcx = get_random();\n  ctx.gprState.rdx = get_random();\n  ctx.gprState.rsi = get_random();\n  ctx.gprState.rdi = get_random();\n  ctx.gprState.r8 = get_random();\n  ctx.gprState.r9 = get_random();\n  ctx.gprState.r10 = get_random();\n  ctx.gprState.r11 = get_random();\n  ctx.gprState.r12 = get_random();\n  ctx.gprState.r13 = get_random();\n  ctx.gprState.r14 = get_random();\n  ctx.gprState.r15 = get_random();\n}\n\nconst char *GPRSave_s =\n    \"    mov $0x1, %rax\\n\"\n    \"    mov $0x2, %rbx\\n\"\n    \"    mov $0x3, %rcx\\n\"\n    \"    mov $0x4, %rdx\\n\"\n    \"    mov $0x5, %rsi\\n\"\n    \"    mov $0x6, %rdi\\n\"\n    \"    mov $0x7, %r8\\n\"\n    \"    mov $0x8, %r9\\n\"\n    \"    mov $0x9, %r10\\n\"\n    \"    mov $0xa, %r11\\n\"\n    \"    mov $0xb, %r12\\n\"\n    \"    mov $0xc, %r13\\n\"\n    \"    mov $0xd, %r14\\n\"\n    \"    mov $0xe, %r15\\n\";\n\nconst char *GPRShuffle_s =\n    \"    push %rax\\n\"\n    \"    push %rbx\\n\"\n    \"    push %rcx\\n\"\n    \"    push %rdx\\n\"\n    \"    push %rsi\\n\"\n    \"    push %rdi\\n\"\n    \"    push %r8\\n\"\n    \"    push %r9\\n\"\n    \"    push %r10\\n\"\n    \"    push %r11\\n\"\n    \"    push %r12\\n\"\n    \"    push %r13\\n\"\n    \"    push %r14\\n\"\n    \"    push %r15\\n\"\n    \"    pop %r14\\n\"\n    \"    pop %r13\\n\"\n    \"    pop %r12\\n\"\n    \"    pop %r11\\n\"\n    \"    pop %r10\\n\"\n    \"    pop %r9\\n\"\n    \"    pop %r8\\n\"\n    \"    pop %rdi\\n\"\n    \"    pop %rsi\\n\"\n    \"    pop %rdx\\n\"\n    \"    pop %rcx\\n\"\n    \"    pop %rbx\\n\"\n    \"    pop %rax\\n\"\n    \"    pop %r15\\n\";\n\nconst char *RelativeAddressing_s =\n    \"jmp start\\n\"\n    \"c1:\\n\"\n    \"    .quad 0x123456789abcdef0\\n\"\n    \"start:\\n\"\n    \"    leaq c1(%rip), %rsi\\n\"\n    \"    movq c1(%rip), %rcx\\n\"\n    \"    xor %rcx, %rax\\n\"\n    \"    xor %rsi, %rsi\\n\"\n    \"    leaq c2(%rip), %rdi\\n\"\n    \"    movq c2(%rip), %rdx\\n\"\n    \"    xor %rdx, %rbx\\n\"\n    \"    xor %rdi, %rbx\\n\"\n    \"    jmp end\\n\"\n    \"c2:\\n\"\n    \"    .quad 0x0fedcba987654321\\n\"\n    \"end:\\n\";\n\nconst char *ConditionalBranching_s =\n    \"    push %rdx\\n\"\n    \"    push %rcx\\n\"\n    \"    push %rbx\\n\"\n    \"    push %rax\\n\"\n    \"    xor %rcx, %rcx\\n\"\n    \"    xor %rdx, %rdx\\n\"\n    \"loop:\\n\"\n    \"    movzx (%rsp), %ax\\n\"\n    \"    inc %rsp\\n\"\n    \"    xor %al, %dl\\n\"\n    \"    ror $12, %rdx\\n\"\n    \"    inc %rcx\\n\"\n    \"    cmpq $32, %rcx\\n\"\n    \"    jb loop\\n\"\n    \"    lea checksum(%rip), %rsi\\n\"\n    \"    mov (%rsi), %rdi\\n\"\n    \"    cmp %rdx, %rdi\\n\"\n    \"    jne bad\\n\"\n    \"    mov $1, %rax\\n\"\n    \"    jmp end\\n\"\n    \"bad:\\n\"\n    \"    mov $0, %rax\\n\"\n    \"    jmp end\\n\"\n    \"checksum:\\n\"\n    \"    .quad 0x32253676ffe8dd7f\\n\"\n    \"end:\\n\";\n\nconst char *FibonacciRecursion_s =\n    \"   lea fibo(%rip), %rbx\\n\"\n    \"   push %rbx\\n\"\n    \"   call *0x0(%rsp)\\n\"\n    \"   jmp end\\n\"\n    \"fibo:\\n\"\n    \"   sub $0x10, %rsp\\n\"\n    \"   cmp $2, %rax\\n\"\n    \"   ja fibo1\\n\"\n    \"   mov $1, %rax\\n\"\n    \"   jmp fibo2\\n\"\n    \"fibo1:\\n\"\n    \"   dec %rax\\n\"\n    \"   mov %rax, 0x8(%rsp)\\n\"\n    \"   call fibo\\n\"\n    \"   mov %rax, 0x0(%rsp)\\n\"\n    \"   mov 0x8(%rsp), %rax\\n\"\n    \"   dec %rax\\n\"\n    \"   lea fibo(%rip), %rbx\\n\"\n    \"   call *%rbx\\n\"\n    \"   add 0x0(%rsp), %rax\\n\"\n    \"fibo2:\\n\"\n    \"   add $0x10, %rsp\\n\"\n    \"   ret\\n\"\n    \"end:\\n\"\n    \"   pop %rbx\\n\";\n\nconst char *StackTricks_s =\n    \"   lea end(%rip), %rcx\\n\"\n    \"   lea f1(%rip), %rdx\\n\"\n    \"   push %rax\\n\"\n    \"   push %rcx\\n\"\n    \"   push %rdx\\n\"\n    \"   ret\\n\"\n    \"f1:\\n\"\n    \"   mov 0x8(%rsp), %rax\\n\"\n    \"   lea f2(%rip), %rcx\\n\"\n    \"   lea f6(%rip), %rdx\\n\"\n    \"   mov $1, %r8\\n\"\n    \"   cmp $2, %rax\\n\"\n    \"   cmova %rcx, %rdx\\n\"\n    \"   push %rdx\\n\"\n    \"   ret\\n\"\n    \"f2:\\n\"\n    \"   dec %rax\\n\"\n    \"   lea f4(%rip), %rcx\\n\"\n    \"   lea f1(%rip), %rdx\\n\"\n    \"   push %rax\\n\"\n    \"   push %rcx\\n\"\n    \"   push %rdx\\n\"\n    \"   ret\\n\"\n    \"f4:\\n\"\n    \"   add %r8, %rbx\\n\"\n    \"   dec %rax\\n\"\n    \"   lea f5(%rip), %rcx\\n\"\n    \"   lea f1(%rip), %rdx\\n\"\n    \"   push %rax\\n\"\n    \"   push %rcx\\n\"\n    \"   push %rdx\\n\"\n    \"   ret\\n\"\n    \"f5:\\n\"\n    \"   add %rbx, %r8\\n\"\n    \"f6:\\n\"\n    \"   mov 0x8(%rsp), %rax\\n\"\n    \"   ret $0x8\\n\"\n    \"end:\\n\";\n\n#define UF1                         \\\n  \"leaq 0x2(%rip), %rax\\n\"          \\\n  \"movabsq    $0x11234bb48, %rbx\\n\" \\\n  \"jmp *%rax\\n\"\n\n#define UF16 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1 UF1\n#define UF256                                                                \\\n  UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 UF16 \\\n      UF16\n\nconst char *UnalignedCodeForward_s =\n    \"    call f2\\n\"\n    \"    call f1\\n\"\n    \"    jmp end\\n\"\n    \"f1:\\n\" UF256 \"f2:\\n\" UF16\n    \"    ret\\n\"\n    \"end:\\n\";\n\n#undef UF1\n#undef UF16\n#undef UF256\n\n#define UB                                 \\\n  \"movabsq    $0xc3c7489090909090, %rbx\\n\" \\\n  \"movabsq    $0xbbeb909090909090, %rbx\\n\" \\\n  \"leaq -0x2f(%rip), %rax\\n\"               \\\n  \"movabsq    $0xc3c7489090909090, %rbx\\n\" \\\n  \"movabsq    $0xc3c7489090909090, %rbx\\n\"\n#define UB1 UB \"jmp *%rax\\n\"\n#define UB16 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1 UB1\n#define UB256                                                                \\\n  UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 UB16 \\\n      UB16\n\nconst char *UnalignedCodeBackward_s =\n    \"    leaq endret(%rip), %rdx\\n\"\n    \"    call f1\\n\"\n    \"    call f2\\n\"\n    \"    jmp end\\n\" UB \"jmp *%rdx\\n\" UB16 \"f1:\\n\" UB256 \"f2:\\n\" UB1\n    \"endret:\\n\"\n    \"    ret\\n\"\n    \"end:\\n\";\n\n#undef UB\n#undef UB1\n#undef UB16\n#undef UB256\n\nconst char *LoopCode_s =\n    \"    push %rcx\\n\"\n    \"    mov $0x0, %rax\\n\"\n    \"    mov $0x5, %rcx\\n\"\n    \"    jrcxz loop_start\\n\"\n    \"    mov $0x10, %rax\\n\"\n    \"loop_start:\\n\"\n    \"    addq $0x1, %rax\\n\"\n    \"    loop loop_start\\n\"\n    \"    jrcxz loop_end1\\n\"\n    \"    shlq\t$0x2, %rax\\n\"\n    \"loop_end1:\\n\"\n    \"    pop %rcx\\n\";\n"
  },
  {
    "path": "test/Patch/X86_64/ComparedExecutor_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef COMPAREDEXECUTOR_X86_64_H\n#define COMPAREDEXECUTOR_X86_64_H\n\n#include <sstream>\n#include <string.h>\n#include <string>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n#include \"TestSetup/ShellcodeTester.h\"\n\nextern const char *GPRSave_s;\nextern const char *GPRShuffle_s;\nextern const char *RelativeAddressing_s;\nextern const char *ConditionalBranching_s;\nextern const char *FibonacciRecursion_s;\nextern const char *StackTricks_s;\nextern const char *UnalignedCodeForward_s;\nextern const char *UnalignedCodeBackward_s;\nextern const char *LoopCode_s;\n\nclass ComparedExecutor_X86_64 : public ShellcodeTester {\n\npublic:\n  QBDI::Context jitExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                        llvm::sys::MemoryBlock &stack);\n\n  QBDI::Context realExec(llvm::ArrayRef<uint8_t> code, QBDI::Context &inputCtx,\n                         llvm::sys::MemoryBlock &stack);\n\n  InMemoryObject compileWithContextSwitch(const char *source);\n\n  void initContext(QBDI::Context &ctx);\n};\n\n#endif\n"
  },
  {
    "path": "test/Patch/X86_64/Instr_Test_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"Patch/Instr_Test.h\"\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-UnalignedCodeForward_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(UnalignedCodeForward_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-UnalignedCodeBackward_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(UnalignedCodeBackward_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n\n#ifndef QBDI_PLATFORM_MACOS\nTEST_CASE_METHOD(Instr_Test, \"Instr_Test-LoopCode_IC\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  uint64_t count1 = 0;\n  uint64_t count2 = 0;\n\n  QBDI::Context inputState;\n  initContext(inputState);\n\n  vm.deleteAllInstrumentations();\n  vm.addCodeCB(QBDI::PREINST, increment, (void *)&count1);\n  vm.addCodeCB(QBDI::POSTINST, increment, (void *)&count2);\n\n  comparedExec(LoopCode_s, inputState, 4096);\n\n  REQUIRE((uint64_t)0 < count1);\n  REQUIRE(count1 == count2);\n\n  INFO(\"Took \" << count1 << \" instructions\\n\");\n}\n#endif\n"
  },
  {
    "path": "test/Patch/X86_64/LLVMOperandInfo_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <set>\n\n#include <catch2/catch_test_macros.hpp>\n#include <stdio.h>\n\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"TestSetup/LLVMTestEnv.h\"\n#include \"Patch/InstInfo.h\"\n\nclass LLVMOperandInfoCheck : public LLVMTestEnv {\nprotected:\n  void checkTIEDOperand();\n};\n\n// LLVM MCInstrDesc specify some operand are TIED_TO another one. We want to\n// verify the followed properties:\n//\n// - If the operation is variadic, the last operand isn't TIED_TO another one.\n// - Two operands can't be TIED_TO to the same operand\n// - An operand is TIED_TO an previous operand\n//\n// An instruction with TIED_TO operand must be in one of theses two cases:\n// - The operand that is tied are the first in the list, and no futher operand.\n//    -> In this case, the instAnalysis skip the tieded operand\n// - Each tied operand is tied to the previous operand.\n//    -> In this case, the instAnalysis merge the operand with the previous one.\n\nvoid LLVMOperandInfoCheck::checkTIEDOperand() {\n\n  const llvm::MCInstrInfo &MCII = getCPU(QBDI::CPUMode::DEFAULT).getMCII();\n\n  for (unsigned opcode = 0; opcode < llvm::X86::INSTRUCTION_LIST_END;\n       opcode++) {\n\n    const llvm::MCInstrDesc &desc = MCII.get(opcode);\n    const char *mnemonic = MCII.getName(opcode).data();\n    const int numOperand = desc.getNumOperands();\n\n    // the opcode is a pseudo instruction used by LLVM internally\n    if (desc.isPseudo())\n      continue;\n\n    if (desc.isVariadic() and numOperand != 0) {\n      if (desc.getOperandConstraint(numOperand - 1, llvm::MCOI::TIED_TO) !=\n          -1) {\n        FAIL_CHECK(\n            \"Instruction \"\n            << mnemonic\n            << \" is variadic but the last operand is tied to another one.\");\n      }\n      for (int opn = 0; opn < numOperand; opn++) {\n        if (desc.getOperandConstraint(opn, llvm::MCOI::TIED_TO) ==\n            numOperand - 1) {\n          FAIL_CHECK(\"Instruction \" << mnemonic\n                                    << \" is variadic but the operand \" << opn\n                                    << \"is tied to the variadic operand.\");\n        }\n      }\n    }\n\n    bool tiedtoPreviousOperand = true;\n    int numTied = 0;\n\n    for (int opn = 0; opn < numOperand; opn++) {\n      int opnTied = desc.getOperandConstraint(opn, llvm::MCOI::TIED_TO);\n      if (opnTied != -1) {\n        numTied++;\n        if (opnTied >= opn) {\n          FAIL_CHECK(\"Instruction \" << mnemonic << \" has the operand \" << opn\n                                    << \" tied to the next operand \" << opnTied\n                                    << \".\");\n        }\n        if (opnTied != opn - 1) {\n          tiedtoPreviousOperand = false;\n        }\n\n        for (int opn2 = opn + 1; opn2 < numOperand; opn2++) {\n          int opnTied2 = desc.getOperandConstraint(opn2, llvm::MCOI::TIED_TO);\n          if (opnTied == opnTied2) {\n            FAIL_CHECK(\"Instruction \" << mnemonic << \" has operands \" << opn\n                                      << \" and \" << opn2\n                                      << \" both tied to the same operand\");\n          }\n        }\n      }\n    }\n\n    int sequencTied = 0;\n    for (int opn = 0; opn < numTied; opn++) {\n      bool found = false;\n      for (int op = opn + 1; op < numOperand; op++) {\n        if (desc.getOperandConstraint(op, llvm::MCOI::TIED_TO) == opn) {\n          sequencTied++;\n          found = true;\n          break;\n        }\n      }\n      if (not found) {\n        break;\n      }\n    }\n\n    if ((sequencTied != numTied) and (not tiedtoPreviousOperand)) {\n      FAIL_CHECK(\"Instruction \"\n                 << mnemonic\n                 << \" fail to verify Bias or Previous Operand property.\");\n    }\n  }\n}\n\nTEST_CASE_METHOD(LLVMOperandInfoCheck, \"LLVMOperandInfo-CrossCheck\") {\n  checkTIEDOperand();\n}\n"
  },
  {
    "path": "test/Patch/X86_64/MemoryAccessTable_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <set>\n#include <stdio.h>\n\n#include \"X86InstrInfo.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/MC/MCInstrDesc.h\"\n\n#include \"Patch/InstInfo.h\"\n#include \"Patch/MemoryAccessTable.h\"\n\nnamespace {\n\nusing namespace llvm::X86;\n\nconst std::set<unsigned> unsupportedInst{\n    // clang-format off\n    // codeGenOnly\n    // alias for other instruction, will never be dissassemble by LLVM\n    LOCK_BTC_RM16rm,\n    LOCK_BTC_RM32rm,\n    LOCK_BTC_RM64rm,\n    LOCK_BTR_RM16rm,\n    LOCK_BTR_RM32rm,\n    LOCK_BTR_RM64rm,\n    LOCK_BTS_RM16rm,\n    LOCK_BTS_RM32rm,\n    LOCK_BTS_RM64rm,\n    LXADD16,\n    LXADD32,\n    LXADD64,\n    LXADD8,\n    MAXCPDrm,\n    MAXCPSrm,\n    MAXCSDrm,\n    MAXCSSrm,\n    MINCPDrm,\n    MINCPSrm,\n    MINCSDrm,\n    MINCSSrm,\n    MMX_MOVD64from64mr,\n    MMX_MOVD64to64rm,\n    MOV64toPQIrm,\n    MOVPQIto64mr,\n    MOVSX16rm16,\n    MOVSX16rm32,\n    MOVSX32rm32,\n    MOVZX16rm16,\n    RDSSPD,\n    RDSSPQ,\n    REP_MOVSB_32,\n    REP_MOVSB_64,\n    REP_MOVSD_32,\n    REP_MOVSD_64,\n    REP_MOVSQ_32,\n    REP_MOVSQ_64,\n    REP_MOVSW_32,\n    REP_MOVSW_64,\n    REP_STOSB_32,\n    REP_STOSB_64,\n    REP_STOSD_32,\n    REP_STOSD_64,\n    REP_STOSQ_32,\n    REP_STOSQ_64,\n    REP_STOSW_32,\n    REP_STOSW_64,\n    SBB8mi8,\n    VMAXCPDYrm,\n    VMAXCPDrm,\n    VMAXCPSYrm,\n    VMAXCPSrm,\n    VMAXCSDrm,\n    VMAXCSSrm,\n    VMINCPDYrm,\n    VMINCPDrm,\n    VMINCPSYrm,\n    VMINCPSrm,\n    VMINCSDrm,\n    VMINCSSrm,\n    VMOV64toPQIrm,\n    VMOVPQIto64mr,\n    // priviledged instruction\n    INVPCID32,\n    RDMSRLIST,\n    VMREAD32mr,\n    VMREAD64mr,\n    VMWRITE32rm,\n    VMWRITE64rm,\n    WBINVD,\n    WBNOINVD,\n    WRMSRLIST,\n    // CET feature (shadow stack)\n    CLRSSBSY,\n    INCSSPD,\n    INCSSPQ,\n    RSTORSSP,\n    SAVEPREVSSP,\n    SETSSBSY,\n    WRSSD,\n    WRSSD_EVEX,\n    WRSSQ,\n    WRSSQ_EVEX,\n    WRUSSD,\n    WRUSSD_EVEX,\n    WRUSSQ,\n    WRUSSQ_EVEX,\n    // RTM feature unsupported\n    XABORT,\n    XBEGIN,\n    XEND,\n    // AVX512 unsupported\n    V4FMADDPSrm,\n    V4FMADDPSrmk,\n    V4FMADDPSrmkz,\n    V4FMADDSSrm,\n    V4FMADDSSrmk,\n    V4FMADDSSrmkz,\n    V4FNMADDPSrm,\n    V4FNMADDPSrmk,\n    V4FNMADDPSrmkz,\n    V4FNMADDSSrm,\n    V4FNMADDSSrmk,\n    V4FNMADDSSrmkz,\n    VADDPDZ128rm,\n    VADDPDZ128rmb,\n    VADDPDZ128rmbk,\n    VADDPDZ128rmbkz,\n    VADDPDZ128rmk,\n    VADDPDZ128rmkz,\n    VADDPDZ256rm,\n    VADDPDZ256rmb,\n    VADDPDZ256rmbk,\n    VADDPDZ256rmbkz,\n    VADDPDZ256rmk,\n    VADDPDZ256rmkz,\n    VADDPDZrm,\n    VADDPDZrmb,\n    VADDPDZrmbk,\n    VADDPDZrmbkz,\n    VADDPDZrmk,\n    VADDPDZrmkz,\n    VADDPHZ128rm,\n    VADDPHZ128rmb,\n    VADDPHZ128rmbk,\n    VADDPHZ128rmbkz,\n    VADDPHZ128rmk,\n    VADDPHZ128rmkz,\n    VADDPHZ256rm,\n    VADDPHZ256rmb,\n    VADDPHZ256rmbk,\n    VADDPHZ256rmbkz,\n    VADDPHZ256rmk,\n    VADDPHZ256rmkz,\n    VADDPHZrm,\n    VADDPHZrmb,\n    VADDPHZrmbk,\n    VADDPHZrmbkz,\n    VADDPHZrmk,\n    VADDPHZrmkz,\n    VADDPSZ128rm,\n    VADDPSZ128rmb,\n    VADDPSZ128rmbk,\n    VADDPSZ128rmbkz,\n    VADDPSZ128rmk,\n    VADDPSZ128rmkz,\n    VADDPSZ256rm,\n    VADDPSZ256rmb,\n    VADDPSZ256rmbk,\n    VADDPSZ256rmbkz,\n    VADDPSZ256rmk,\n    VADDPSZ256rmkz,\n    VADDPSZrm,\n    VADDPSZrmb,\n    VADDPSZrmbk,\n    VADDPSZrmbkz,\n    VADDPSZrmk,\n    VADDPSZrmkz,\n    VADDSDZrm_Int,\n    VADDSDZrm_Intk,\n    VADDSDZrm_Intkz,\n    VADDSHZrm_Int,\n    VADDSHZrm_Intk,\n    VADDSHZrm_Intkz,\n    VADDSSZrm_Int,\n    VADDSSZrm_Intk,\n    VADDSSZrm_Intkz,\n    VALIGNDZ128rmbi,\n    VALIGNDZ128rmbik,\n    VALIGNDZ128rmbikz,\n    VALIGNDZ128rmi,\n    VALIGNDZ128rmik,\n    VALIGNDZ128rmikz,\n    VALIGNDZ256rmbi,\n    VALIGNDZ256rmbik,\n    VALIGNDZ256rmbikz,\n    VALIGNDZ256rmi,\n    VALIGNDZ256rmik,\n    VALIGNDZ256rmikz,\n    VALIGNDZrmbi,\n    VALIGNDZrmbik,\n    VALIGNDZrmbikz,\n    VALIGNDZrmi,\n    VALIGNDZrmik,\n    VALIGNDZrmikz,\n    VALIGNQZ128rmbi,\n    VALIGNQZ128rmbik,\n    VALIGNQZ128rmbikz,\n    VALIGNQZ128rmi,\n    VALIGNQZ128rmik,\n    VALIGNQZ128rmikz,\n    VALIGNQZ256rmbi,\n    VALIGNQZ256rmbik,\n    VALIGNQZ256rmbikz,\n    VALIGNQZ256rmi,\n    VALIGNQZ256rmik,\n    VALIGNQZ256rmikz,\n    VALIGNQZrmbi,\n    VALIGNQZrmbik,\n    VALIGNQZrmbikz,\n    VALIGNQZrmi,\n    VALIGNQZrmik,\n    VALIGNQZrmikz,\n    VANDNPDZ128rm,\n    VANDNPDZ128rmb,\n    VANDNPDZ128rmbk,\n    VANDNPDZ128rmbkz,\n    VANDNPDZ128rmk,\n    VANDNPDZ128rmkz,\n    VANDNPDZ256rm,\n    VANDNPDZ256rmb,\n    VANDNPDZ256rmbk,\n    VANDNPDZ256rmbkz,\n    VANDNPDZ256rmk,\n    VANDNPDZ256rmkz,\n    VANDNPDZrm,\n    VANDNPDZrmb,\n    VANDNPDZrmbk,\n    VANDNPDZrmbkz,\n    VANDNPDZrmk,\n    VANDNPDZrmkz,\n    VANDNPSZ128rm,\n    VANDNPSZ128rmb,\n    VANDNPSZ128rmbk,\n    VANDNPSZ128rmbkz,\n    VANDNPSZ128rmk,\n    VANDNPSZ128rmkz,\n    VANDNPSZ256rm,\n    VANDNPSZ256rmb,\n    VANDNPSZ256rmbk,\n    VANDNPSZ256rmbkz,\n    VANDNPSZ256rmk,\n    VANDNPSZ256rmkz,\n    VANDNPSZrm,\n    VANDNPSZrmb,\n    VANDNPSZrmbk,\n    VANDNPSZrmbkz,\n    VANDNPSZrmk,\n    VANDNPSZrmkz,\n    VANDPDZ128rm,\n    VANDPDZ128rmb,\n    VANDPDZ128rmbk,\n    VANDPDZ128rmbkz,\n    VANDPDZ128rmk,\n    VANDPDZ128rmkz,\n    VANDPDZ256rm,\n    VANDPDZ256rmb,\n    VANDPDZ256rmbk,\n    VANDPDZ256rmbkz,\n    VANDPDZ256rmk,\n    VANDPDZ256rmkz,\n    VANDPDZrm,\n    VANDPDZrmb,\n    VANDPDZrmbk,\n    VANDPDZrmbkz,\n    VANDPDZrmk,\n    VANDPDZrmkz,\n    VANDPSZ128rm,\n    VANDPSZ128rmb,\n    VANDPSZ128rmbk,\n    VANDPSZ128rmbkz,\n    VANDPSZ128rmk,\n    VANDPSZ128rmkz,\n    VANDPSZ256rm,\n    VANDPSZ256rmb,\n    VANDPSZ256rmbk,\n    VANDPSZ256rmbkz,\n    VANDPSZ256rmk,\n    VANDPSZ256rmkz,\n    VANDPSZrm,\n    VANDPSZrmb,\n    VANDPSZrmbk,\n    VANDPSZrmbkz,\n    VANDPSZrmk,\n    VANDPSZrmkz,\n    VBLENDMPDZ128rm,\n    VBLENDMPDZ128rmb,\n    VBLENDMPDZ128rmbk,\n    VBLENDMPDZ128rmbkz,\n    VBLENDMPDZ128rmk,\n    VBLENDMPDZ128rmkz,\n    VBLENDMPDZ256rm,\n    VBLENDMPDZ256rmb,\n    VBLENDMPDZ256rmbk,\n    VBLENDMPDZ256rmbkz,\n    VBLENDMPDZ256rmk,\n    VBLENDMPDZ256rmkz,\n    VBLENDMPDZrm,\n    VBLENDMPDZrmb,\n    VBLENDMPDZrmbk,\n    VBLENDMPDZrmbkz,\n    VBLENDMPDZrmk,\n    VBLENDMPDZrmkz,\n    VBLENDMPSZ128rm,\n    VBLENDMPSZ128rmb,\n    VBLENDMPSZ128rmbk,\n    VBLENDMPSZ128rmbkz,\n    VBLENDMPSZ128rmk,\n    VBLENDMPSZ128rmkz,\n    VBLENDMPSZ256rm,\n    VBLENDMPSZ256rmb,\n    VBLENDMPSZ256rmbk,\n    VBLENDMPSZ256rmbkz,\n    VBLENDMPSZ256rmk,\n    VBLENDMPSZ256rmkz,\n    VBLENDMPSZrm,\n    VBLENDMPSZrmb,\n    VBLENDMPSZrmbk,\n    VBLENDMPSZrmbkz,\n    VBLENDMPSZrmk,\n    VBLENDMPSZrmkz,\n    VBROADCASTF32X2Z256rm,\n    VBROADCASTF32X2Z256rmk,\n    VBROADCASTF32X2Z256rmkz,\n    VBROADCASTF32X2Zrm,\n    VBROADCASTF32X2Zrmk,\n    VBROADCASTF32X2Zrmkz,\n    VBROADCASTF32X4Z256rm,\n    VBROADCASTF32X4Z256rmk,\n    VBROADCASTF32X4Z256rmkz,\n    VBROADCASTF32X4rm,\n    VBROADCASTF32X4rmk,\n    VBROADCASTF32X4rmkz,\n    VBROADCASTF32X8rm,\n    VBROADCASTF32X8rmk,\n    VBROADCASTF32X8rmkz,\n    VBROADCASTF64X2Z128rm,\n    VBROADCASTF64X2Z128rmk,\n    VBROADCASTF64X2Z128rmkz,\n    VBROADCASTF64X2rm,\n    VBROADCASTF64X2rmk,\n    VBROADCASTF64X2rmkz,\n    VBROADCASTF64X4rm,\n    VBROADCASTF64X4rmk,\n    VBROADCASTF64X4rmkz,\n    VBROADCASTI32X2Z128rm,\n    VBROADCASTI32X2Z128rmk,\n    VBROADCASTI32X2Z128rmkz,\n    VBROADCASTI32X2Z256rm,\n    VBROADCASTI32X2Z256rmk,\n    VBROADCASTI32X2Z256rmkz,\n    VBROADCASTI32X2Zrm,\n    VBROADCASTI32X2Zrmk,\n    VBROADCASTI32X2Zrmkz,\n    VBROADCASTI32X4Z256rm,\n    VBROADCASTI32X4Z256rmk,\n    VBROADCASTI32X4Z256rmkz,\n    VBROADCASTI32X4rm,\n    VBROADCASTI32X4rmk,\n    VBROADCASTI32X4rmkz,\n    VBROADCASTI32X8rm,\n    VBROADCASTI32X8rmk,\n    VBROADCASTI32X8rmkz,\n    VBROADCASTI64X2Z128rm,\n    VBROADCASTI64X2Z128rmk,\n    VBROADCASTI64X2Z128rmkz,\n    VBROADCASTI64X2rm,\n    VBROADCASTI64X2rmk,\n    VBROADCASTI64X2rmkz,\n    VBROADCASTI64X4rm,\n    VBROADCASTI64X4rmk,\n    VBROADCASTI64X4rmkz,\n    VBROADCASTSDZ256rm,\n    VBROADCASTSDZ256rmk,\n    VBROADCASTSDZ256rmkz,\n    VBROADCASTSDZrm,\n    VBROADCASTSDZrmk,\n    VBROADCASTSDZrmkz,\n    VBROADCASTSSZ128rm,\n    VBROADCASTSSZ128rmk,\n    VBROADCASTSSZ128rmkz,\n    VBROADCASTSSZ256rm,\n    VBROADCASTSSZ256rmk,\n    VBROADCASTSSZ256rmkz,\n    VBROADCASTSSZrm,\n    VBROADCASTSSZrmk,\n    VBROADCASTSSZrmkz,\n    VCMPPDZ128rmbi,\n    VCMPPDZ128rmbik,\n    VCMPPDZ128rmi,\n    VCMPPDZ128rmik,\n    VCMPPDZ256rmbi,\n    VCMPPDZ256rmbik,\n    VCMPPDZ256rmi,\n    VCMPPDZ256rmik,\n    VCMPPDZrmbi,\n    VCMPPDZrmbik,\n    VCMPPDZrmi,\n    VCMPPDZrmik,\n    VCMPPHZ128rmbi,\n    VCMPPHZ128rmbik,\n    VCMPPHZ128rmi,\n    VCMPPHZ128rmik,\n    VCMPPHZ256rmbi,\n    VCMPPHZ256rmbik,\n    VCMPPHZ256rmi,\n    VCMPPHZ256rmik,\n    VCMPPHZrmbi,\n    VCMPPHZrmbik,\n    VCMPPHZrmi,\n    VCMPPHZrmik,\n    VCMPPSZ128rmbi,\n    VCMPPSZ128rmbik,\n    VCMPPSZ128rmi,\n    VCMPPSZ128rmik,\n    VCMPPSZ256rmbi,\n    VCMPPSZ256rmbik,\n    VCMPPSZ256rmi,\n    VCMPPSZ256rmik,\n    VCMPPSZrmbi,\n    VCMPPSZrmbik,\n    VCMPPSZrmi,\n    VCMPPSZrmik,\n    VCMPSDZrmi,\n    VCMPSDZrmi_Int,\n    VCMPSDZrmi_Intk,\n    VCMPSHZrmi,\n    VCMPSHZrmi_Int,\n    VCMPSHZrmi_Intk,\n    VCMPSSZrmi,\n    VCMPSSZrmi_Int,\n    VCMPSSZrmi_Intk,\n    VCOMPRESSPDZ128mr,\n    VCOMPRESSPDZ128mrk,\n    VCOMPRESSPDZ256mr,\n    VCOMPRESSPDZ256mrk,\n    VCOMPRESSPDZmr,\n    VCOMPRESSPDZmrk,\n    VCOMPRESSPSZ128mr,\n    VCOMPRESSPSZ128mrk,\n    VCOMPRESSPSZ256mr,\n    VCOMPRESSPSZ256mrk,\n    VCOMPRESSPSZmr,\n    VCOMPRESSPSZmrk,\n    VCVTDQ2PDZ128rm,\n    VCVTDQ2PDZ128rmb,\n    VCVTDQ2PDZ128rmbk,\n    VCVTDQ2PDZ128rmbkz,\n    VCVTDQ2PDZ128rmk,\n    VCVTDQ2PDZ128rmkz,\n    VCVTDQ2PDZ256rm,\n    VCVTDQ2PDZ256rmb,\n    VCVTDQ2PDZ256rmbk,\n    VCVTDQ2PDZ256rmbkz,\n    VCVTDQ2PDZ256rmk,\n    VCVTDQ2PDZ256rmkz,\n    VCVTDQ2PDZrm,\n    VCVTDQ2PDZrmb,\n    VCVTDQ2PDZrmbk,\n    VCVTDQ2PDZrmbkz,\n    VCVTDQ2PDZrmk,\n    VCVTDQ2PDZrmkz,\n    VCVTDQ2PHZ128rm,\n    VCVTDQ2PHZ128rmb,\n    VCVTDQ2PHZ128rmbk,\n    VCVTDQ2PHZ128rmbkz,\n    VCVTDQ2PHZ128rmk,\n    VCVTDQ2PHZ128rmkz,\n    VCVTDQ2PHZ256rm,\n    VCVTDQ2PHZ256rmb,\n    VCVTDQ2PHZ256rmbk,\n    VCVTDQ2PHZ256rmbkz,\n    VCVTDQ2PHZ256rmk,\n    VCVTDQ2PHZ256rmkz,\n    VCVTDQ2PHZrm,\n    VCVTDQ2PHZrmb,\n    VCVTDQ2PHZrmbk,\n    VCVTDQ2PHZrmbkz,\n    VCVTDQ2PHZrmk,\n    VCVTDQ2PHZrmkz,\n    VCVTDQ2PSZ128rm,\n    VCVTDQ2PSZ128rmb,\n    VCVTDQ2PSZ128rmbk,\n    VCVTDQ2PSZ128rmbkz,\n    VCVTDQ2PSZ128rmk,\n    VCVTDQ2PSZ128rmkz,\n    VCVTDQ2PSZ256rm,\n    VCVTDQ2PSZ256rmb,\n    VCVTDQ2PSZ256rmbk,\n    VCVTDQ2PSZ256rmbkz,\n    VCVTDQ2PSZ256rmk,\n    VCVTDQ2PSZ256rmkz,\n    VCVTDQ2PSZrm,\n    VCVTDQ2PSZrmb,\n    VCVTDQ2PSZrmbk,\n    VCVTDQ2PSZrmbkz,\n    VCVTDQ2PSZrmk,\n    VCVTDQ2PSZrmkz,\n    VCVTNE2PS2BF16Z128rm,\n    VCVTNE2PS2BF16Z128rmb,\n    VCVTNE2PS2BF16Z128rmbk,\n    VCVTNE2PS2BF16Z128rmbkz,\n    VCVTNE2PS2BF16Z128rmk,\n    VCVTNE2PS2BF16Z128rmkz,\n    VCVTNE2PS2BF16Z256rm,\n    VCVTNE2PS2BF16Z256rmb,\n    VCVTNE2PS2BF16Z256rmbk,\n    VCVTNE2PS2BF16Z256rmbkz,\n    VCVTNE2PS2BF16Z256rmk,\n    VCVTNE2PS2BF16Z256rmkz,\n    VCVTNE2PS2BF16Zrm,\n    VCVTNE2PS2BF16Zrmb,\n    VCVTNE2PS2BF16Zrmbk,\n    VCVTNE2PS2BF16Zrmbkz,\n    VCVTNE2PS2BF16Zrmk,\n    VCVTNE2PS2BF16Zrmkz,\n    VCVTNEPS2BF16Z128rm,\n    VCVTNEPS2BF16Z128rmb,\n    VCVTNEPS2BF16Z128rmbk,\n    VCVTNEPS2BF16Z128rmbkz,\n    VCVTNEPS2BF16Z128rmk,\n    VCVTNEPS2BF16Z128rmkz,\n    VCVTNEPS2BF16Z256rm,\n    VCVTNEPS2BF16Z256rmb,\n    VCVTNEPS2BF16Z256rmbk,\n    VCVTNEPS2BF16Z256rmbkz,\n    VCVTNEPS2BF16Z256rmk,\n    VCVTNEPS2BF16Z256rmkz,\n    VCVTNEPS2BF16Zrm,\n    VCVTNEPS2BF16Zrmb,\n    VCVTNEPS2BF16Zrmbk,\n    VCVTNEPS2BF16Zrmbkz,\n    VCVTNEPS2BF16Zrmk,\n    VCVTNEPS2BF16Zrmkz,\n    VCVTPD2DQZ128rm,\n    VCVTPD2DQZ128rmb,\n    VCVTPD2DQZ128rmbk,\n    VCVTPD2DQZ128rmbkz,\n    VCVTPD2DQZ128rmk,\n    VCVTPD2DQZ128rmkz,\n    VCVTPD2DQZ256rm,\n    VCVTPD2DQZ256rmb,\n    VCVTPD2DQZ256rmbk,\n    VCVTPD2DQZ256rmbkz,\n    VCVTPD2DQZ256rmk,\n    VCVTPD2DQZ256rmkz,\n    VCVTPD2DQZrm,\n    VCVTPD2DQZrmb,\n    VCVTPD2DQZrmbk,\n    VCVTPD2DQZrmbkz,\n    VCVTPD2DQZrmk,\n    VCVTPD2DQZrmkz,\n    VCVTPD2PHZ128rm,\n    VCVTPD2PHZ128rmb,\n    VCVTPD2PHZ128rmbk,\n    VCVTPD2PHZ128rmbkz,\n    VCVTPD2PHZ128rmk,\n    VCVTPD2PHZ128rmkz,\n    VCVTPD2PHZ256rm,\n    VCVTPD2PHZ256rmb,\n    VCVTPD2PHZ256rmbk,\n    VCVTPD2PHZ256rmbkz,\n    VCVTPD2PHZ256rmk,\n    VCVTPD2PHZ256rmkz,\n    VCVTPD2PHZrm,\n    VCVTPD2PHZrmb,\n    VCVTPD2PHZrmbk,\n    VCVTPD2PHZrmbkz,\n    VCVTPD2PHZrmk,\n    VCVTPD2PHZrmkz,\n    VCVTPD2PSZ128rm,\n    VCVTPD2PSZ128rmb,\n    VCVTPD2PSZ128rmbk,\n    VCVTPD2PSZ128rmbkz,\n    VCVTPD2PSZ128rmk,\n    VCVTPD2PSZ128rmkz,\n    VCVTPD2PSZ256rm,\n    VCVTPD2PSZ256rmb,\n    VCVTPD2PSZ256rmbk,\n    VCVTPD2PSZ256rmbkz,\n    VCVTPD2PSZ256rmk,\n    VCVTPD2PSZ256rmkz,\n    VCVTPD2PSZrm,\n    VCVTPD2PSZrmb,\n    VCVTPD2PSZrmbk,\n    VCVTPD2PSZrmbkz,\n    VCVTPD2PSZrmk,\n    VCVTPD2PSZrmkz,\n    VCVTPD2QQZ128rm,\n    VCVTPD2QQZ128rmb,\n    VCVTPD2QQZ128rmbk,\n    VCVTPD2QQZ128rmbkz,\n    VCVTPD2QQZ128rmk,\n    VCVTPD2QQZ128rmkz,\n    VCVTPD2QQZ256rm,\n    VCVTPD2QQZ256rmb,\n    VCVTPD2QQZ256rmbk,\n    VCVTPD2QQZ256rmbkz,\n    VCVTPD2QQZ256rmk,\n    VCVTPD2QQZ256rmkz,\n    VCVTPD2QQZrm,\n    VCVTPD2QQZrmb,\n    VCVTPD2QQZrmbk,\n    VCVTPD2QQZrmbkz,\n    VCVTPD2QQZrmk,\n    VCVTPD2QQZrmkz,\n    VCVTPD2UDQZ128rm,\n    VCVTPD2UDQZ128rmb,\n    VCVTPD2UDQZ128rmbk,\n    VCVTPD2UDQZ128rmbkz,\n    VCVTPD2UDQZ128rmk,\n    VCVTPD2UDQZ128rmkz,\n    VCVTPD2UDQZ256rm,\n    VCVTPD2UDQZ256rmb,\n    VCVTPD2UDQZ256rmbk,\n    VCVTPD2UDQZ256rmbkz,\n    VCVTPD2UDQZ256rmk,\n    VCVTPD2UDQZ256rmkz,\n    VCVTPD2UDQZrm,\n    VCVTPD2UDQZrmb,\n    VCVTPD2UDQZrmbk,\n    VCVTPD2UDQZrmbkz,\n    VCVTPD2UDQZrmk,\n    VCVTPD2UDQZrmkz,\n    VCVTPD2UQQZ128rm,\n    VCVTPD2UQQZ128rmb,\n    VCVTPD2UQQZ128rmbk,\n    VCVTPD2UQQZ128rmbkz,\n    VCVTPD2UQQZ128rmk,\n    VCVTPD2UQQZ128rmkz,\n    VCVTPD2UQQZ256rm,\n    VCVTPD2UQQZ256rmb,\n    VCVTPD2UQQZ256rmbk,\n    VCVTPD2UQQZ256rmbkz,\n    VCVTPD2UQQZ256rmk,\n    VCVTPD2UQQZ256rmkz,\n    VCVTPD2UQQZrm,\n    VCVTPD2UQQZrmb,\n    VCVTPD2UQQZrmbk,\n    VCVTPD2UQQZrmbkz,\n    VCVTPD2UQQZrmk,\n    VCVTPD2UQQZrmkz,\n    VCVTPH2DQZ128rm,\n    VCVTPH2DQZ128rmb,\n    VCVTPH2DQZ128rmbk,\n    VCVTPH2DQZ128rmbkz,\n    VCVTPH2DQZ128rmk,\n    VCVTPH2DQZ128rmkz,\n    VCVTPH2DQZ256rm,\n    VCVTPH2DQZ256rmb,\n    VCVTPH2DQZ256rmbk,\n    VCVTPH2DQZ256rmbkz,\n    VCVTPH2DQZ256rmk,\n    VCVTPH2DQZ256rmkz,\n    VCVTPH2DQZrm,\n    VCVTPH2DQZrmb,\n    VCVTPH2DQZrmbk,\n    VCVTPH2DQZrmbkz,\n    VCVTPH2DQZrmk,\n    VCVTPH2DQZrmkz,\n    VCVTPH2PDZ128rm,\n    VCVTPH2PDZ128rmb,\n    VCVTPH2PDZ128rmbk,\n    VCVTPH2PDZ128rmbkz,\n    VCVTPH2PDZ128rmk,\n    VCVTPH2PDZ128rmkz,\n    VCVTPH2PDZ256rm,\n    VCVTPH2PDZ256rmb,\n    VCVTPH2PDZ256rmbk,\n    VCVTPH2PDZ256rmbkz,\n    VCVTPH2PDZ256rmk,\n    VCVTPH2PDZ256rmkz,\n    VCVTPH2PDZrm,\n    VCVTPH2PDZrmb,\n    VCVTPH2PDZrmbk,\n    VCVTPH2PDZrmbkz,\n    VCVTPH2PDZrmk,\n    VCVTPH2PDZrmkz,\n    VCVTPH2PSXZ128rm,\n    VCVTPH2PSXZ128rmb,\n    VCVTPH2PSXZ128rmbk,\n    VCVTPH2PSXZ128rmbkz,\n    VCVTPH2PSXZ128rmk,\n    VCVTPH2PSXZ128rmkz,\n    VCVTPH2PSXZ256rm,\n    VCVTPH2PSXZ256rmb,\n    VCVTPH2PSXZ256rmbk,\n    VCVTPH2PSXZ256rmbkz,\n    VCVTPH2PSXZ256rmk,\n    VCVTPH2PSXZ256rmkz,\n    VCVTPH2PSXZrm,\n    VCVTPH2PSXZrmb,\n    VCVTPH2PSXZrmbk,\n    VCVTPH2PSXZrmbkz,\n    VCVTPH2PSXZrmk,\n    VCVTPH2PSXZrmkz,\n    VCVTPH2PSZ128rm,\n    VCVTPH2PSZ128rmk,\n    VCVTPH2PSZ128rmkz,\n    VCVTPH2PSZ256rm,\n    VCVTPH2PSZ256rmk,\n    VCVTPH2PSZ256rmkz,\n    VCVTPH2PSZrm,\n    VCVTPH2PSZrmk,\n    VCVTPH2PSZrmkz,\n    VCVTPH2QQZ128rm,\n    VCVTPH2QQZ128rmb,\n    VCVTPH2QQZ128rmbk,\n    VCVTPH2QQZ128rmbkz,\n    VCVTPH2QQZ128rmk,\n    VCVTPH2QQZ128rmkz,\n    VCVTPH2QQZ256rm,\n    VCVTPH2QQZ256rmb,\n    VCVTPH2QQZ256rmbk,\n    VCVTPH2QQZ256rmbkz,\n    VCVTPH2QQZ256rmk,\n    VCVTPH2QQZ256rmkz,\n    VCVTPH2QQZrm,\n    VCVTPH2QQZrmb,\n    VCVTPH2QQZrmbk,\n    VCVTPH2QQZrmbkz,\n    VCVTPH2QQZrmk,\n    VCVTPH2QQZrmkz,\n    VCVTPH2UDQZ128rm,\n    VCVTPH2UDQZ128rmb,\n    VCVTPH2UDQZ128rmbk,\n    VCVTPH2UDQZ128rmbkz,\n    VCVTPH2UDQZ128rmk,\n    VCVTPH2UDQZ128rmkz,\n    VCVTPH2UDQZ256rm,\n    VCVTPH2UDQZ256rmb,\n    VCVTPH2UDQZ256rmbk,\n    VCVTPH2UDQZ256rmbkz,\n    VCVTPH2UDQZ256rmk,\n    VCVTPH2UDQZ256rmkz,\n    VCVTPH2UDQZrm,\n    VCVTPH2UDQZrmb,\n    VCVTPH2UDQZrmbk,\n    VCVTPH2UDQZrmbkz,\n    VCVTPH2UDQZrmk,\n    VCVTPH2UDQZrmkz,\n    VCVTPH2UQQZ128rm,\n    VCVTPH2UQQZ128rmb,\n    VCVTPH2UQQZ128rmbk,\n    VCVTPH2UQQZ128rmbkz,\n    VCVTPH2UQQZ128rmk,\n    VCVTPH2UQQZ128rmkz,\n    VCVTPH2UQQZ256rm,\n    VCVTPH2UQQZ256rmb,\n    VCVTPH2UQQZ256rmbk,\n    VCVTPH2UQQZ256rmbkz,\n    VCVTPH2UQQZ256rmk,\n    VCVTPH2UQQZ256rmkz,\n    VCVTPH2UQQZrm,\n    VCVTPH2UQQZrmb,\n    VCVTPH2UQQZrmbk,\n    VCVTPH2UQQZrmbkz,\n    VCVTPH2UQQZrmk,\n    VCVTPH2UQQZrmkz,\n    VCVTPH2UWZ128rm,\n    VCVTPH2UWZ128rmb,\n    VCVTPH2UWZ128rmbk,\n    VCVTPH2UWZ128rmbkz,\n    VCVTPH2UWZ128rmk,\n    VCVTPH2UWZ128rmkz,\n    VCVTPH2UWZ256rm,\n    VCVTPH2UWZ256rmb,\n    VCVTPH2UWZ256rmbk,\n    VCVTPH2UWZ256rmbkz,\n    VCVTPH2UWZ256rmk,\n    VCVTPH2UWZ256rmkz,\n    VCVTPH2UWZrm,\n    VCVTPH2UWZrmb,\n    VCVTPH2UWZrmbk,\n    VCVTPH2UWZrmbkz,\n    VCVTPH2UWZrmk,\n    VCVTPH2UWZrmkz,\n    VCVTPH2WZ128rm,\n    VCVTPH2WZ128rmb,\n    VCVTPH2WZ128rmbk,\n    VCVTPH2WZ128rmbkz,\n    VCVTPH2WZ128rmk,\n    VCVTPH2WZ128rmkz,\n    VCVTPH2WZ256rm,\n    VCVTPH2WZ256rmb,\n    VCVTPH2WZ256rmbk,\n    VCVTPH2WZ256rmbkz,\n    VCVTPH2WZ256rmk,\n    VCVTPH2WZ256rmkz,\n    VCVTPH2WZrm,\n    VCVTPH2WZrmb,\n    VCVTPH2WZrmbk,\n    VCVTPH2WZrmbkz,\n    VCVTPH2WZrmk,\n    VCVTPH2WZrmkz,\n    VCVTPS2DQZ128rm,\n    VCVTPS2DQZ128rmb,\n    VCVTPS2DQZ128rmbk,\n    VCVTPS2DQZ128rmbkz,\n    VCVTPS2DQZ128rmk,\n    VCVTPS2DQZ128rmkz,\n    VCVTPS2DQZ256rm,\n    VCVTPS2DQZ256rmb,\n    VCVTPS2DQZ256rmbk,\n    VCVTPS2DQZ256rmbkz,\n    VCVTPS2DQZ256rmk,\n    VCVTPS2DQZ256rmkz,\n    VCVTPS2DQZrm,\n    VCVTPS2DQZrmb,\n    VCVTPS2DQZrmbk,\n    VCVTPS2DQZrmbkz,\n    VCVTPS2DQZrmk,\n    VCVTPS2DQZrmkz,\n    VCVTPS2PDZ128rm,\n    VCVTPS2PDZ128rmb,\n    VCVTPS2PDZ128rmbk,\n    VCVTPS2PDZ128rmbkz,\n    VCVTPS2PDZ128rmk,\n    VCVTPS2PDZ128rmkz,\n    VCVTPS2PDZ256rm,\n    VCVTPS2PDZ256rmb,\n    VCVTPS2PDZ256rmbk,\n    VCVTPS2PDZ256rmbkz,\n    VCVTPS2PDZ256rmk,\n    VCVTPS2PDZ256rmkz,\n    VCVTPS2PDZrm,\n    VCVTPS2PDZrmb,\n    VCVTPS2PDZrmbk,\n    VCVTPS2PDZrmbkz,\n    VCVTPS2PDZrmk,\n    VCVTPS2PDZrmkz,\n    VCVTPS2PHXZ128rm,\n    VCVTPS2PHXZ128rmb,\n    VCVTPS2PHXZ128rmbk,\n    VCVTPS2PHXZ128rmbkz,\n    VCVTPS2PHXZ128rmk,\n    VCVTPS2PHXZ128rmkz,\n    VCVTPS2PHXZ256rm,\n    VCVTPS2PHXZ256rmb,\n    VCVTPS2PHXZ256rmbk,\n    VCVTPS2PHXZ256rmbkz,\n    VCVTPS2PHXZ256rmk,\n    VCVTPS2PHXZ256rmkz,\n    VCVTPS2PHXZrm,\n    VCVTPS2PHXZrmb,\n    VCVTPS2PHXZrmbk,\n    VCVTPS2PHXZrmbkz,\n    VCVTPS2PHXZrmk,\n    VCVTPS2PHXZrmkz,\n    VCVTPS2PHZ128mr,\n    VCVTPS2PHZ128mrk,\n    VCVTPS2PHZ256mr,\n    VCVTPS2PHZ256mrk,\n    VCVTPS2PHZmr,\n    VCVTPS2PHZmrk,\n    VCVTPS2QQZ128rm,\n    VCVTPS2QQZ128rmb,\n    VCVTPS2QQZ128rmbk,\n    VCVTPS2QQZ128rmbkz,\n    VCVTPS2QQZ128rmk,\n    VCVTPS2QQZ128rmkz,\n    VCVTPS2QQZ256rm,\n    VCVTPS2QQZ256rmb,\n    VCVTPS2QQZ256rmbk,\n    VCVTPS2QQZ256rmbkz,\n    VCVTPS2QQZ256rmk,\n    VCVTPS2QQZ256rmkz,\n    VCVTPS2QQZrm,\n    VCVTPS2QQZrmb,\n    VCVTPS2QQZrmbk,\n    VCVTPS2QQZrmbkz,\n    VCVTPS2QQZrmk,\n    VCVTPS2QQZrmkz,\n    VCVTPS2UDQZ128rm,\n    VCVTPS2UDQZ128rmb,\n    VCVTPS2UDQZ128rmbk,\n    VCVTPS2UDQZ128rmbkz,\n    VCVTPS2UDQZ128rmk,\n    VCVTPS2UDQZ128rmkz,\n    VCVTPS2UDQZ256rm,\n    VCVTPS2UDQZ256rmb,\n    VCVTPS2UDQZ256rmbk,\n    VCVTPS2UDQZ256rmbkz,\n    VCVTPS2UDQZ256rmk,\n    VCVTPS2UDQZ256rmkz,\n    VCVTPS2UDQZrm,\n    VCVTPS2UDQZrmb,\n    VCVTPS2UDQZrmbk,\n    VCVTPS2UDQZrmbkz,\n    VCVTPS2UDQZrmk,\n    VCVTPS2UDQZrmkz,\n    VCVTPS2UQQZ128rm,\n    VCVTPS2UQQZ128rmb,\n    VCVTPS2UQQZ128rmbk,\n    VCVTPS2UQQZ128rmbkz,\n    VCVTPS2UQQZ128rmk,\n    VCVTPS2UQQZ128rmkz,\n    VCVTPS2UQQZ256rm,\n    VCVTPS2UQQZ256rmb,\n    VCVTPS2UQQZ256rmbk,\n    VCVTPS2UQQZ256rmbkz,\n    VCVTPS2UQQZ256rmk,\n    VCVTPS2UQQZ256rmkz,\n    VCVTPS2UQQZrm,\n    VCVTPS2UQQZrmb,\n    VCVTPS2UQQZrmbk,\n    VCVTPS2UQQZrmbkz,\n    VCVTPS2UQQZrmk,\n    VCVTPS2UQQZrmkz,\n    VCVTQQ2PDZ128rm,\n    VCVTQQ2PDZ128rmb,\n    VCVTQQ2PDZ128rmbk,\n    VCVTQQ2PDZ128rmbkz,\n    VCVTQQ2PDZ128rmk,\n    VCVTQQ2PDZ128rmkz,\n    VCVTQQ2PDZ256rm,\n    VCVTQQ2PDZ256rmb,\n    VCVTQQ2PDZ256rmbk,\n    VCVTQQ2PDZ256rmbkz,\n    VCVTQQ2PDZ256rmk,\n    VCVTQQ2PDZ256rmkz,\n    VCVTQQ2PDZrm,\n    VCVTQQ2PDZrmb,\n    VCVTQQ2PDZrmbk,\n    VCVTQQ2PDZrmbkz,\n    VCVTQQ2PDZrmk,\n    VCVTQQ2PDZrmkz,\n    VCVTQQ2PHZ128rm,\n    VCVTQQ2PHZ128rmb,\n    VCVTQQ2PHZ128rmbk,\n    VCVTQQ2PHZ128rmbkz,\n    VCVTQQ2PHZ128rmk,\n    VCVTQQ2PHZ128rmkz,\n    VCVTQQ2PHZ256rm,\n    VCVTQQ2PHZ256rmb,\n    VCVTQQ2PHZ256rmbk,\n    VCVTQQ2PHZ256rmbkz,\n    VCVTQQ2PHZ256rmk,\n    VCVTQQ2PHZ256rmkz,\n    VCVTQQ2PHZrm,\n    VCVTQQ2PHZrmb,\n    VCVTQQ2PHZrmbk,\n    VCVTQQ2PHZrmbkz,\n    VCVTQQ2PHZrmk,\n    VCVTQQ2PHZrmkz,\n    VCVTQQ2PSZ128rm,\n    VCVTQQ2PSZ128rmb,\n    VCVTQQ2PSZ128rmbk,\n    VCVTQQ2PSZ128rmbkz,\n    VCVTQQ2PSZ128rmk,\n    VCVTQQ2PSZ128rmkz,\n    VCVTQQ2PSZ256rm,\n    VCVTQQ2PSZ256rmb,\n    VCVTQQ2PSZ256rmbk,\n    VCVTQQ2PSZ256rmbkz,\n    VCVTQQ2PSZ256rmk,\n    VCVTQQ2PSZ256rmkz,\n    VCVTQQ2PSZrm,\n    VCVTQQ2PSZrmb,\n    VCVTQQ2PSZrmbk,\n    VCVTQQ2PSZrmbkz,\n    VCVTQQ2PSZrmk,\n    VCVTQQ2PSZrmkz,\n    VCVTSD2SHZrm_Int,\n    VCVTSD2SHZrm_Intk,\n    VCVTSD2SHZrm_Intkz,\n    VCVTSD2SI64Zrm,\n    VCVTSD2SIZrm,\n    VCVTSD2SSZrm_Int,\n    VCVTSD2SSZrm_Intk,\n    VCVTSD2SSZrm_Intkz,\n    VCVTSH2SDZrm_Int,\n    VCVTSH2SDZrm_Intk,\n    VCVTSH2SDZrm_Intkz,\n    VCVTSH2SSZrm_Int,\n    VCVTSH2SSZrm_Intk,\n    VCVTSH2SSZrm_Intkz,\n    VCVTSS2SDZrm_Int,\n    VCVTSS2SDZrm_Intk,\n    VCVTSS2SDZrm_Intkz,\n    VCVTSS2SHZrm_Int,\n    VCVTSS2SHZrm_Intk,\n    VCVTSS2SHZrm_Intkz,\n    VCVTSS2SI64Zrm,\n    VCVTSS2SIZrm,\n    VCVTTPD2DQZ128rm,\n    VCVTTPD2DQZ128rmb,\n    VCVTTPD2DQZ128rmbk,\n    VCVTTPD2DQZ128rmbkz,\n    VCVTTPD2DQZ128rmk,\n    VCVTTPD2DQZ128rmkz,\n    VCVTTPD2DQZ256rm,\n    VCVTTPD2DQZ256rmb,\n    VCVTTPD2DQZ256rmbk,\n    VCVTTPD2DQZ256rmbkz,\n    VCVTTPD2DQZ256rmk,\n    VCVTTPD2DQZ256rmkz,\n    VCVTTPD2DQZrm,\n    VCVTTPD2DQZrmb,\n    VCVTTPD2DQZrmbk,\n    VCVTTPD2DQZrmbkz,\n    VCVTTPD2DQZrmk,\n    VCVTTPD2DQZrmkz,\n    VCVTTPD2QQZ128rm,\n    VCVTTPD2QQZ128rmb,\n    VCVTTPD2QQZ128rmbk,\n    VCVTTPD2QQZ128rmbkz,\n    VCVTTPD2QQZ128rmk,\n    VCVTTPD2QQZ128rmkz,\n    VCVTTPD2QQZ256rm,\n    VCVTTPD2QQZ256rmb,\n    VCVTTPD2QQZ256rmbk,\n    VCVTTPD2QQZ256rmbkz,\n    VCVTTPD2QQZ256rmk,\n    VCVTTPD2QQZ256rmkz,\n    VCVTTPD2QQZrm,\n    VCVTTPD2QQZrmb,\n    VCVTTPD2QQZrmbk,\n    VCVTTPD2QQZrmbkz,\n    VCVTTPD2QQZrmk,\n    VCVTTPD2QQZrmkz,\n    VCVTTPD2UDQZ128rm,\n    VCVTTPD2UDQZ128rmb,\n    VCVTTPD2UDQZ128rmbk,\n    VCVTTPD2UDQZ128rmbkz,\n    VCVTTPD2UDQZ128rmk,\n    VCVTTPD2UDQZ128rmkz,\n    VCVTTPD2UDQZ256rm,\n    VCVTTPD2UDQZ256rmb,\n    VCVTTPD2UDQZ256rmbk,\n    VCVTTPD2UDQZ256rmbkz,\n    VCVTTPD2UDQZ256rmk,\n    VCVTTPD2UDQZ256rmkz,\n    VCVTTPD2UDQZrm,\n    VCVTTPD2UDQZrmb,\n    VCVTTPD2UDQZrmbk,\n    VCVTTPD2UDQZrmbkz,\n    VCVTTPD2UDQZrmk,\n    VCVTTPD2UDQZrmkz,\n    VCVTTPD2UQQZ128rm,\n    VCVTTPD2UQQZ128rmb,\n    VCVTTPD2UQQZ128rmbk,\n    VCVTTPD2UQQZ128rmbkz,\n    VCVTTPD2UQQZ128rmk,\n    VCVTTPD2UQQZ128rmkz,\n    VCVTTPD2UQQZ256rm,\n    VCVTTPD2UQQZ256rmb,\n    VCVTTPD2UQQZ256rmbk,\n    VCVTTPD2UQQZ256rmbkz,\n    VCVTTPD2UQQZ256rmk,\n    VCVTTPD2UQQZ256rmkz,\n    VCVTTPD2UQQZrm,\n    VCVTTPD2UQQZrmb,\n    VCVTTPD2UQQZrmbk,\n    VCVTTPD2UQQZrmbkz,\n    VCVTTPD2UQQZrmk,\n    VCVTTPD2UQQZrmkz,\n    VCVTTPH2DQZ128rm,\n    VCVTTPH2DQZ128rmb,\n    VCVTTPH2DQZ128rmbk,\n    VCVTTPH2DQZ128rmbkz,\n    VCVTTPH2DQZ128rmk,\n    VCVTTPH2DQZ128rmkz,\n    VCVTTPH2DQZ256rm,\n    VCVTTPH2DQZ256rmb,\n    VCVTTPH2DQZ256rmbk,\n    VCVTTPH2DQZ256rmbkz,\n    VCVTTPH2DQZ256rmk,\n    VCVTTPH2DQZ256rmkz,\n    VCVTTPH2DQZrm,\n    VCVTTPH2DQZrmb,\n    VCVTTPH2DQZrmbk,\n    VCVTTPH2DQZrmbkz,\n    VCVTTPH2DQZrmk,\n    VCVTTPH2DQZrmkz,\n    VCVTTPH2QQZ128rm,\n    VCVTTPH2QQZ128rmb,\n    VCVTTPH2QQZ128rmbk,\n    VCVTTPH2QQZ128rmbkz,\n    VCVTTPH2QQZ128rmk,\n    VCVTTPH2QQZ128rmkz,\n    VCVTTPH2QQZ256rm,\n    VCVTTPH2QQZ256rmb,\n    VCVTTPH2QQZ256rmbk,\n    VCVTTPH2QQZ256rmbkz,\n    VCVTTPH2QQZ256rmk,\n    VCVTTPH2QQZ256rmkz,\n    VCVTTPH2QQZrm,\n    VCVTTPH2QQZrmb,\n    VCVTTPH2QQZrmbk,\n    VCVTTPH2QQZrmbkz,\n    VCVTTPH2QQZrmk,\n    VCVTTPH2QQZrmkz,\n    VCVTTPH2UDQZ128rm,\n    VCVTTPH2UDQZ128rmb,\n    VCVTTPH2UDQZ128rmbk,\n    VCVTTPH2UDQZ128rmbkz,\n    VCVTTPH2UDQZ128rmk,\n    VCVTTPH2UDQZ128rmkz,\n    VCVTTPH2UDQZ256rm,\n    VCVTTPH2UDQZ256rmb,\n    VCVTTPH2UDQZ256rmbk,\n    VCVTTPH2UDQZ256rmbkz,\n    VCVTTPH2UDQZ256rmk,\n    VCVTTPH2UDQZ256rmkz,\n    VCVTTPH2UDQZrm,\n    VCVTTPH2UDQZrmb,\n    VCVTTPH2UDQZrmbk,\n    VCVTTPH2UDQZrmbkz,\n    VCVTTPH2UDQZrmk,\n    VCVTTPH2UDQZrmkz,\n    VCVTTPH2UQQZ128rm,\n    VCVTTPH2UQQZ128rmb,\n    VCVTTPH2UQQZ128rmbk,\n    VCVTTPH2UQQZ128rmbkz,\n    VCVTTPH2UQQZ128rmk,\n    VCVTTPH2UQQZ128rmkz,\n    VCVTTPH2UQQZ256rm,\n    VCVTTPH2UQQZ256rmb,\n    VCVTTPH2UQQZ256rmbk,\n    VCVTTPH2UQQZ256rmbkz,\n    VCVTTPH2UQQZ256rmk,\n    VCVTTPH2UQQZ256rmkz,\n    VCVTTPH2UQQZrm,\n    VCVTTPH2UQQZrmb,\n    VCVTTPH2UQQZrmbk,\n    VCVTTPH2UQQZrmbkz,\n    VCVTTPH2UQQZrmk,\n    VCVTTPH2UQQZrmkz,\n    VCVTTPH2UWZ128rm,\n    VCVTTPH2UWZ128rmb,\n    VCVTTPH2UWZ128rmbk,\n    VCVTTPH2UWZ128rmbkz,\n    VCVTTPH2UWZ128rmk,\n    VCVTTPH2UWZ128rmkz,\n    VCVTTPH2UWZ256rm,\n    VCVTTPH2UWZ256rmb,\n    VCVTTPH2UWZ256rmbk,\n    VCVTTPH2UWZ256rmbkz,\n    VCVTTPH2UWZ256rmk,\n    VCVTTPH2UWZ256rmkz,\n    VCVTTPH2UWZrm,\n    VCVTTPH2UWZrmb,\n    VCVTTPH2UWZrmbk,\n    VCVTTPH2UWZrmbkz,\n    VCVTTPH2UWZrmk,\n    VCVTTPH2UWZrmkz,\n    VCVTTPH2WZ128rm,\n    VCVTTPH2WZ128rmb,\n    VCVTTPH2WZ128rmbk,\n    VCVTTPH2WZ128rmbkz,\n    VCVTTPH2WZ128rmk,\n    VCVTTPH2WZ128rmkz,\n    VCVTTPH2WZ256rm,\n    VCVTTPH2WZ256rmb,\n    VCVTTPH2WZ256rmbk,\n    VCVTTPH2WZ256rmbkz,\n    VCVTTPH2WZ256rmk,\n    VCVTTPH2WZ256rmkz,\n    VCVTTPH2WZrm,\n    VCVTTPH2WZrmb,\n    VCVTTPH2WZrmbk,\n    VCVTTPH2WZrmbkz,\n    VCVTTPH2WZrmk,\n    VCVTTPH2WZrmkz,\n    VCVTTPS2DQZ128rm,\n    VCVTTPS2DQZ128rmb,\n    VCVTTPS2DQZ128rmbk,\n    VCVTTPS2DQZ128rmbkz,\n    VCVTTPS2DQZ128rmk,\n    VCVTTPS2DQZ128rmkz,\n    VCVTTPS2DQZ256rm,\n    VCVTTPS2DQZ256rmb,\n    VCVTTPS2DQZ256rmbk,\n    VCVTTPS2DQZ256rmbkz,\n    VCVTTPS2DQZ256rmk,\n    VCVTTPS2DQZ256rmkz,\n    VCVTTPS2DQZrm,\n    VCVTTPS2DQZrmb,\n    VCVTTPS2DQZrmbk,\n    VCVTTPS2DQZrmbkz,\n    VCVTTPS2DQZrmk,\n    VCVTTPS2DQZrmkz,\n    VCVTTPS2QQZ128rm,\n    VCVTTPS2QQZ128rmb,\n    VCVTTPS2QQZ128rmbk,\n    VCVTTPS2QQZ128rmbkz,\n    VCVTTPS2QQZ128rmk,\n    VCVTTPS2QQZ128rmkz,\n    VCVTTPS2QQZ256rm,\n    VCVTTPS2QQZ256rmb,\n    VCVTTPS2QQZ256rmbk,\n    VCVTTPS2QQZ256rmbkz,\n    VCVTTPS2QQZ256rmk,\n    VCVTTPS2QQZ256rmkz,\n    VCVTTPS2QQZrm,\n    VCVTTPS2QQZrmb,\n    VCVTTPS2QQZrmbk,\n    VCVTTPS2QQZrmbkz,\n    VCVTTPS2QQZrmk,\n    VCVTTPS2QQZrmkz,\n    VCVTTPS2UDQZ128rm,\n    VCVTTPS2UDQZ128rmb,\n    VCVTTPS2UDQZ128rmbk,\n    VCVTTPS2UDQZ128rmbkz,\n    VCVTTPS2UDQZ128rmk,\n    VCVTTPS2UDQZ128rmkz,\n    VCVTTPS2UDQZ256rm,\n    VCVTTPS2UDQZ256rmb,\n    VCVTTPS2UDQZ256rmbk,\n    VCVTTPS2UDQZ256rmbkz,\n    VCVTTPS2UDQZ256rmk,\n    VCVTTPS2UDQZ256rmkz,\n    VCVTTPS2UDQZrm,\n    VCVTTPS2UDQZrmb,\n    VCVTTPS2UDQZrmbk,\n    VCVTTPS2UDQZrmbkz,\n    VCVTTPS2UDQZrmk,\n    VCVTTPS2UDQZrmkz,\n    VCVTTPS2UQQZ128rm,\n    VCVTTPS2UQQZ128rmb,\n    VCVTTPS2UQQZ128rmbk,\n    VCVTTPS2UQQZ128rmbkz,\n    VCVTTPS2UQQZ128rmk,\n    VCVTTPS2UQQZ128rmkz,\n    VCVTTPS2UQQZ256rm,\n    VCVTTPS2UQQZ256rmb,\n    VCVTTPS2UQQZ256rmbk,\n    VCVTTPS2UQQZ256rmbkz,\n    VCVTTPS2UQQZ256rmk,\n    VCVTTPS2UQQZ256rmkz,\n    VCVTTPS2UQQZrm,\n    VCVTTPS2UQQZrmb,\n    VCVTTPS2UQQZrmbk,\n    VCVTTPS2UQQZrmbkz,\n    VCVTTPS2UQQZrmk,\n    VCVTTPS2UQQZrmkz,\n    VCVTTSD2SI64Zrm,\n    VCVTTSD2SI64Zrm_Int,\n    VCVTTSD2SIZrm,\n    VCVTTSD2SIZrm_Int,\n    VCVTTSD2USI64Zrm,\n    VCVTTSD2USI64Zrm_Int,\n    VCVTTSD2USIZrm,\n    VCVTTSD2USIZrm_Int,\n    VCVTTSH2SI64Zrm,\n    VCVTTSH2SI64Zrm_Int,\n    VCVTTSH2SIZrm,\n    VCVTTSH2SIZrm_Int,\n    VCVTTSH2USI64Zrm,\n    VCVTTSH2USI64Zrm_Int,\n    VCVTTSH2USIZrm,\n    VCVTTSH2USIZrm_Int,\n    VCVTTSS2SI64Zrm,\n    VCVTTSS2SI64Zrm_Int,\n    VCVTTSS2SIZrm,\n    VCVTTSS2SIZrm_Int,\n    VCVTTSS2USI64Zrm,\n    VCVTTSS2USI64Zrm_Int,\n    VCVTTSS2USIZrm,\n    VCVTTSS2USIZrm_Int,\n    VCVTUDQ2PDZ128rm,\n    VCVTUDQ2PDZ128rmb,\n    VCVTUDQ2PDZ128rmbk,\n    VCVTUDQ2PDZ128rmbkz,\n    VCVTUDQ2PDZ128rmk,\n    VCVTUDQ2PDZ128rmkz,\n    VCVTUDQ2PDZ256rm,\n    VCVTUDQ2PDZ256rmb,\n    VCVTUDQ2PDZ256rmbk,\n    VCVTUDQ2PDZ256rmbkz,\n    VCVTUDQ2PDZ256rmk,\n    VCVTUDQ2PDZ256rmkz,\n    VCVTUDQ2PDZrm,\n    VCVTUDQ2PDZrmb,\n    VCVTUDQ2PDZrmbk,\n    VCVTUDQ2PDZrmbkz,\n    VCVTUDQ2PDZrmk,\n    VCVTUDQ2PDZrmkz,\n    VCVTUDQ2PHZ128rm,\n    VCVTUDQ2PHZ128rmb,\n    VCVTUDQ2PHZ128rmbk,\n    VCVTUDQ2PHZ128rmbkz,\n    VCVTUDQ2PHZ128rmk,\n    VCVTUDQ2PHZ128rmkz,\n    VCVTUDQ2PHZ256rm,\n    VCVTUDQ2PHZ256rmb,\n    VCVTUDQ2PHZ256rmbk,\n    VCVTUDQ2PHZ256rmbkz,\n    VCVTUDQ2PHZ256rmk,\n    VCVTUDQ2PHZ256rmkz,\n    VCVTUDQ2PHZrm,\n    VCVTUDQ2PHZrmb,\n    VCVTUDQ2PHZrmbk,\n    VCVTUDQ2PHZrmbkz,\n    VCVTUDQ2PHZrmk,\n    VCVTUDQ2PHZrmkz,\n    VCVTUDQ2PSZ128rm,\n    VCVTUDQ2PSZ128rmb,\n    VCVTUDQ2PSZ128rmbk,\n    VCVTUDQ2PSZ128rmbkz,\n    VCVTUDQ2PSZ128rmk,\n    VCVTUDQ2PSZ128rmkz,\n    VCVTUDQ2PSZ256rm,\n    VCVTUDQ2PSZ256rmb,\n    VCVTUDQ2PSZ256rmbk,\n    VCVTUDQ2PSZ256rmbkz,\n    VCVTUDQ2PSZ256rmk,\n    VCVTUDQ2PSZ256rmkz,\n    VCVTUDQ2PSZrm,\n    VCVTUDQ2PSZrmb,\n    VCVTUDQ2PSZrmbk,\n    VCVTUDQ2PSZrmbkz,\n    VCVTUDQ2PSZrmk,\n    VCVTUDQ2PSZrmkz,\n    VCVTUQQ2PDZ128rm,\n    VCVTUQQ2PDZ128rmb,\n    VCVTUQQ2PDZ128rmbk,\n    VCVTUQQ2PDZ128rmbkz,\n    VCVTUQQ2PDZ128rmk,\n    VCVTUQQ2PDZ128rmkz,\n    VCVTUQQ2PDZ256rm,\n    VCVTUQQ2PDZ256rmb,\n    VCVTUQQ2PDZ256rmbk,\n    VCVTUQQ2PDZ256rmbkz,\n    VCVTUQQ2PDZ256rmk,\n    VCVTUQQ2PDZ256rmkz,\n    VCVTUQQ2PDZrm,\n    VCVTUQQ2PDZrmb,\n    VCVTUQQ2PDZrmbk,\n    VCVTUQQ2PDZrmbkz,\n    VCVTUQQ2PDZrmk,\n    VCVTUQQ2PDZrmkz,\n    VCVTUQQ2PHZ128rm,\n    VCVTUQQ2PHZ128rmb,\n    VCVTUQQ2PHZ128rmbk,\n    VCVTUQQ2PHZ128rmbkz,\n    VCVTUQQ2PHZ128rmk,\n    VCVTUQQ2PHZ128rmkz,\n    VCVTUQQ2PHZ256rm,\n    VCVTUQQ2PHZ256rmb,\n    VCVTUQQ2PHZ256rmbk,\n    VCVTUQQ2PHZ256rmbkz,\n    VCVTUQQ2PHZ256rmk,\n    VCVTUQQ2PHZ256rmkz,\n    VCVTUQQ2PHZrm,\n    VCVTUQQ2PHZrmb,\n    VCVTUQQ2PHZrmbk,\n    VCVTUQQ2PHZrmbkz,\n    VCVTUQQ2PHZrmk,\n    VCVTUQQ2PHZrmkz,\n    VCVTUQQ2PSZ128rm,\n    VCVTUQQ2PSZ128rmb,\n    VCVTUQQ2PSZ128rmbk,\n    VCVTUQQ2PSZ128rmbkz,\n    VCVTUQQ2PSZ128rmk,\n    VCVTUQQ2PSZ128rmkz,\n    VCVTUQQ2PSZ256rm,\n    VCVTUQQ2PSZ256rmb,\n    VCVTUQQ2PSZ256rmbk,\n    VCVTUQQ2PSZ256rmbkz,\n    VCVTUQQ2PSZ256rmk,\n    VCVTUQQ2PSZ256rmkz,\n    VCVTUQQ2PSZrm,\n    VCVTUQQ2PSZrmb,\n    VCVTUQQ2PSZrmbk,\n    VCVTUQQ2PSZrmbkz,\n    VCVTUQQ2PSZrmk,\n    VCVTUQQ2PSZrmkz,\n    VCVTUW2PHZ128rm,\n    VCVTUW2PHZ128rmb,\n    VCVTUW2PHZ128rmbk,\n    VCVTUW2PHZ128rmbkz,\n    VCVTUW2PHZ128rmk,\n    VCVTUW2PHZ128rmkz,\n    VCVTUW2PHZ256rm,\n    VCVTUW2PHZ256rmb,\n    VCVTUW2PHZ256rmbk,\n    VCVTUW2PHZ256rmbkz,\n    VCVTUW2PHZ256rmk,\n    VCVTUW2PHZ256rmkz,\n    VCVTUW2PHZrm,\n    VCVTUW2PHZrmb,\n    VCVTUW2PHZrmbk,\n    VCVTUW2PHZrmbkz,\n    VCVTUW2PHZrmk,\n    VCVTUW2PHZrmkz,\n    VCVTW2PHZ128rm,\n    VCVTW2PHZ128rmb,\n    VCVTW2PHZ128rmbk,\n    VCVTW2PHZ128rmbkz,\n    VCVTW2PHZ128rmk,\n    VCVTW2PHZ128rmkz,\n    VCVTW2PHZ256rm,\n    VCVTW2PHZ256rmb,\n    VCVTW2PHZ256rmbk,\n    VCVTW2PHZ256rmbkz,\n    VCVTW2PHZ256rmk,\n    VCVTW2PHZ256rmkz,\n    VCVTW2PHZrm,\n    VCVTW2PHZrmb,\n    VCVTW2PHZrmbk,\n    VCVTW2PHZrmbkz,\n    VCVTW2PHZrmk,\n    VCVTW2PHZrmkz,\n    VDBPSADBWZ128rmi,\n    VDBPSADBWZ128rmik,\n    VDBPSADBWZ128rmikz,\n    VDBPSADBWZ256rmi,\n    VDBPSADBWZ256rmik,\n    VDBPSADBWZ256rmikz,\n    VDBPSADBWZrmi,\n    VDBPSADBWZrmik,\n    VDBPSADBWZrmikz,\n    VDIVPDZ128rm,\n    VDIVPDZ128rmb,\n    VDIVPDZ128rmbk,\n    VDIVPDZ128rmbkz,\n    VDIVPDZ128rmk,\n    VDIVPDZ128rmkz,\n    VDIVPDZ256rm,\n    VDIVPDZ256rmb,\n    VDIVPDZ256rmbk,\n    VDIVPDZ256rmbkz,\n    VDIVPDZ256rmk,\n    VDIVPDZ256rmkz,\n    VDIVPDZrm,\n    VDIVPDZrmb,\n    VDIVPDZrmbk,\n    VDIVPDZrmbkz,\n    VDIVPDZrmk,\n    VDIVPDZrmkz,\n    VDIVPHZ128rm,\n    VDIVPHZ128rmb,\n    VDIVPHZ128rmbk,\n    VDIVPHZ128rmbkz,\n    VDIVPHZ128rmk,\n    VDIVPHZ128rmkz,\n    VDIVPHZ256rm,\n    VDIVPHZ256rmb,\n    VDIVPHZ256rmbk,\n    VDIVPHZ256rmbkz,\n    VDIVPHZ256rmk,\n    VDIVPHZ256rmkz,\n    VDIVPHZrm,\n    VDIVPHZrmb,\n    VDIVPHZrmbk,\n    VDIVPHZrmbkz,\n    VDIVPHZrmk,\n    VDIVPHZrmkz,\n    VDIVPSZ128rm,\n    VDIVPSZ128rmb,\n    VDIVPSZ128rmbk,\n    VDIVPSZ128rmbkz,\n    VDIVPSZ128rmk,\n    VDIVPSZ128rmkz,\n    VDIVPSZ256rm,\n    VDIVPSZ256rmb,\n    VDIVPSZ256rmbk,\n    VDIVPSZ256rmbkz,\n    VDIVPSZ256rmk,\n    VDIVPSZ256rmkz,\n    VDIVPSZrm,\n    VDIVPSZrmb,\n    VDIVPSZrmbk,\n    VDIVPSZrmbkz,\n    VDIVPSZrmk,\n    VDIVPSZrmkz,\n    VDIVSDZrm_Int,\n    VDIVSDZrm_Intk,\n    VDIVSDZrm_Intkz,\n    VDIVSHZrm_Int,\n    VDIVSHZrm_Intk,\n    VDIVSHZrm_Intkz,\n    VDIVSSZrm_Int,\n    VDIVSSZrm_Intk,\n    VDIVSSZrm_Intkz,\n    VDPBF16PSZ128m,\n    VDPBF16PSZ128mb,\n    VDPBF16PSZ128mbk,\n    VDPBF16PSZ128mbkz,\n    VDPBF16PSZ128mk,\n    VDPBF16PSZ128mkz,\n    VDPBF16PSZ256m,\n    VDPBF16PSZ256mb,\n    VDPBF16PSZ256mbk,\n    VDPBF16PSZ256mbkz,\n    VDPBF16PSZ256mk,\n    VDPBF16PSZ256mkz,\n    VDPBF16PSZm,\n    VDPBF16PSZmb,\n    VDPBF16PSZmbk,\n    VDPBF16PSZmbkz,\n    VDPBF16PSZmk,\n    VDPBF16PSZmkz,\n    VEXP2PDZm,\n    VEXP2PDZmb,\n    VEXP2PDZmbk,\n    VEXP2PDZmbkz,\n    VEXP2PDZmk,\n    VEXP2PDZmkz,\n    VEXP2PSZm,\n    VEXP2PSZmb,\n    VEXP2PSZmbk,\n    VEXP2PSZmbkz,\n    VEXP2PSZmk,\n    VEXP2PSZmkz,\n    VEXPANDPDZ128rmk,\n    VEXPANDPDZ128rmkz,\n    VEXPANDPDZ256rmk,\n    VEXPANDPDZ256rmkz,\n    VEXPANDPDZrmk,\n    VEXPANDPDZrmkz,\n    VEXPANDPSZ128rmk,\n    VEXPANDPSZ128rmkz,\n    VEXPANDPSZ256rmk,\n    VEXPANDPSZ256rmkz,\n    VEXPANDPSZrmk,\n    VEXPANDPSZrmkz,\n    VEXTRACTF32x4Z256mr,\n    VEXTRACTF32x4Z256mrk,\n    VEXTRACTF32x4Zmr,\n    VEXTRACTF32x4Zmrk,\n    VEXTRACTF32x8Zmrk,\n    VEXTRACTF64x2Z256mrk,\n    VEXTRACTF64x2Zmrk,\n    VEXTRACTF64x4Zmr,\n    VEXTRACTF64x4Zmrk,\n    VEXTRACTI32x4Z256mr,\n    VEXTRACTI32x4Z256mrk,\n    VEXTRACTI32x4Zmr,\n    VEXTRACTI32x4Zmrk,\n    VEXTRACTI32x8Zmrk,\n    VEXTRACTI64x2Z256mrk,\n    VEXTRACTI64x2Zmrk,\n    VEXTRACTI64x4Zmr,\n    VEXTRACTI64x4Zmrk,\n    VEXTRACTPSZmr,\n    VFCMADDCPHZ128m,\n    VFCMADDCPHZ128mb,\n    VFCMADDCPHZ128mbk,\n    VFCMADDCPHZ128mbkz,\n    VFCMADDCPHZ128mk,\n    VFCMADDCPHZ128mkz,\n    VFCMADDCPHZ256m,\n    VFCMADDCPHZ256mb,\n    VFCMADDCPHZ256mbk,\n    VFCMADDCPHZ256mbkz,\n    VFCMADDCPHZ256mk,\n    VFCMADDCPHZ256mkz,\n    VFCMADDCPHZm,\n    VFCMADDCPHZmb,\n    VFCMADDCPHZmbk,\n    VFCMADDCPHZmbkz,\n    VFCMADDCPHZmk,\n    VFCMADDCPHZmkz,\n    VFCMADDCSHZm,\n    VFCMADDCSHZmk,\n    VFCMADDCSHZmkz,\n    VFCMULCPHZ128rm,\n    VFCMULCPHZ128rmb,\n    VFCMULCPHZ128rmbk,\n    VFCMULCPHZ128rmbkz,\n    VFCMULCPHZ128rmk,\n    VFCMULCPHZ128rmkz,\n    VFCMULCPHZ256rm,\n    VFCMULCPHZ256rmb,\n    VFCMULCPHZ256rmbk,\n    VFCMULCPHZ256rmbkz,\n    VFCMULCPHZ256rmk,\n    VFCMULCPHZ256rmkz,\n    VFCMULCPHZrm,\n    VFCMULCPHZrmb,\n    VFCMULCPHZrmbk,\n    VFCMULCPHZrmbkz,\n    VFCMULCPHZrmk,\n    VFCMULCPHZrmkz,\n    VFCMULCSHZrm,\n    VFCMULCSHZrmk,\n    VFCMULCSHZrmkz,\n    VFIXUPIMMPDZ128rmbi,\n    VFIXUPIMMPDZ128rmbik,\n    VFIXUPIMMPDZ128rmbikz,\n    VFIXUPIMMPDZ128rmi,\n    VFIXUPIMMPDZ128rmik,\n    VFIXUPIMMPDZ128rmikz,\n    VFIXUPIMMPDZ256rmbi,\n    VFIXUPIMMPDZ256rmbik,\n    VFIXUPIMMPDZ256rmbikz,\n    VFIXUPIMMPDZ256rmi,\n    VFIXUPIMMPDZ256rmik,\n    VFIXUPIMMPDZ256rmikz,\n    VFIXUPIMMPDZrmbi,\n    VFIXUPIMMPDZrmbik,\n    VFIXUPIMMPDZrmbikz,\n    VFIXUPIMMPDZrmi,\n    VFIXUPIMMPDZrmik,\n    VFIXUPIMMPDZrmikz,\n    VFIXUPIMMPSZ128rmbi,\n    VFIXUPIMMPSZ128rmbik,\n    VFIXUPIMMPSZ128rmbikz,\n    VFIXUPIMMPSZ128rmi,\n    VFIXUPIMMPSZ128rmik,\n    VFIXUPIMMPSZ128rmikz,\n    VFIXUPIMMPSZ256rmbi,\n    VFIXUPIMMPSZ256rmbik,\n    VFIXUPIMMPSZ256rmbikz,\n    VFIXUPIMMPSZ256rmi,\n    VFIXUPIMMPSZ256rmik,\n    VFIXUPIMMPSZ256rmikz,\n    VFIXUPIMMPSZrmbi,\n    VFIXUPIMMPSZrmbik,\n    VFIXUPIMMPSZrmbikz,\n    VFIXUPIMMPSZrmi,\n    VFIXUPIMMPSZrmik,\n    VFIXUPIMMPSZrmikz,\n    VFIXUPIMMSDZrmi,\n    VFIXUPIMMSDZrmik,\n    VFIXUPIMMSDZrmikz,\n    VFIXUPIMMSSZrmi,\n    VFIXUPIMMSSZrmik,\n    VFIXUPIMMSSZrmikz,\n    VFMADD132PDZ128m,\n    VFMADD132PDZ128mb,\n    VFMADD132PDZ128mbk,\n    VFMADD132PDZ128mbkz,\n    VFMADD132PDZ128mk,\n    VFMADD132PDZ128mkz,\n    VFMADD132PDZ256m,\n    VFMADD132PDZ256mb,\n    VFMADD132PDZ256mbk,\n    VFMADD132PDZ256mbkz,\n    VFMADD132PDZ256mk,\n    VFMADD132PDZ256mkz,\n    VFMADD132PDZm,\n    VFMADD132PDZmb,\n    VFMADD132PDZmbk,\n    VFMADD132PDZmbkz,\n    VFMADD132PDZmk,\n    VFMADD132PDZmkz,\n    VFMADD132PHZ128m,\n    VFMADD132PHZ128mb,\n    VFMADD132PHZ128mbk,\n    VFMADD132PHZ128mbkz,\n    VFMADD132PHZ128mk,\n    VFMADD132PHZ128mkz,\n    VFMADD132PHZ256m,\n    VFMADD132PHZ256mb,\n    VFMADD132PHZ256mbk,\n    VFMADD132PHZ256mbkz,\n    VFMADD132PHZ256mk,\n    VFMADD132PHZ256mkz,\n    VFMADD132PHZm,\n    VFMADD132PHZmb,\n    VFMADD132PHZmbk,\n    VFMADD132PHZmbkz,\n    VFMADD132PHZmk,\n    VFMADD132PHZmkz,\n    VFMADD132PSZ128m,\n    VFMADD132PSZ128mb,\n    VFMADD132PSZ128mbk,\n    VFMADD132PSZ128mbkz,\n    VFMADD132PSZ128mk,\n    VFMADD132PSZ128mkz,\n    VFMADD132PSZ256m,\n    VFMADD132PSZ256mb,\n    VFMADD132PSZ256mbk,\n    VFMADD132PSZ256mbkz,\n    VFMADD132PSZ256mk,\n    VFMADD132PSZ256mkz,\n    VFMADD132PSZm,\n    VFMADD132PSZmb,\n    VFMADD132PSZmbk,\n    VFMADD132PSZmbkz,\n    VFMADD132PSZmk,\n    VFMADD132PSZmkz,\n    VFMADD132SDZm,\n    VFMADD132SDZm_Int,\n    VFMADD132SDZm_Intk,\n    VFMADD132SDZm_Intkz,\n    VFMADD132SHZm,\n    VFMADD132SHZm_Int,\n    VFMADD132SHZm_Intk,\n    VFMADD132SHZm_Intkz,\n    VFMADD132SSZm,\n    VFMADD132SSZm_Int,\n    VFMADD132SSZm_Intk,\n    VFMADD132SSZm_Intkz,\n    VFMADD213PDZ128m,\n    VFMADD213PDZ128mb,\n    VFMADD213PDZ128mbk,\n    VFMADD213PDZ128mbkz,\n    VFMADD213PDZ128mk,\n    VFMADD213PDZ128mkz,\n    VFMADD213PDZ256m,\n    VFMADD213PDZ256mb,\n    VFMADD213PDZ256mbk,\n    VFMADD213PDZ256mbkz,\n    VFMADD213PDZ256mk,\n    VFMADD213PDZ256mkz,\n    VFMADD213PDZm,\n    VFMADD213PDZmb,\n    VFMADD213PDZmbk,\n    VFMADD213PDZmbkz,\n    VFMADD213PDZmk,\n    VFMADD213PDZmkz,\n    VFMADD213PHZ128m,\n    VFMADD213PHZ128mb,\n    VFMADD213PHZ128mbk,\n    VFMADD213PHZ128mbkz,\n    VFMADD213PHZ128mk,\n    VFMADD213PHZ128mkz,\n    VFMADD213PHZ256m,\n    VFMADD213PHZ256mb,\n    VFMADD213PHZ256mbk,\n    VFMADD213PHZ256mbkz,\n    VFMADD213PHZ256mk,\n    VFMADD213PHZ256mkz,\n    VFMADD213PHZm,\n    VFMADD213PHZmb,\n    VFMADD213PHZmbk,\n    VFMADD213PHZmbkz,\n    VFMADD213PHZmk,\n    VFMADD213PHZmkz,\n    VFMADD213PSZ128m,\n    VFMADD213PSZ128mb,\n    VFMADD213PSZ128mbk,\n    VFMADD213PSZ128mbkz,\n    VFMADD213PSZ128mk,\n    VFMADD213PSZ128mkz,\n    VFMADD213PSZ256m,\n    VFMADD213PSZ256mb,\n    VFMADD213PSZ256mbk,\n    VFMADD213PSZ256mbkz,\n    VFMADD213PSZ256mk,\n    VFMADD213PSZ256mkz,\n    VFMADD213PSZm,\n    VFMADD213PSZmb,\n    VFMADD213PSZmbk,\n    VFMADD213PSZmbkz,\n    VFMADD213PSZmk,\n    VFMADD213PSZmkz,\n    VFMADD213SDZm,\n    VFMADD213SDZm_Int,\n    VFMADD213SDZm_Intk,\n    VFMADD213SDZm_Intkz,\n    VFMADD213SHZm,\n    VFMADD213SHZm_Int,\n    VFMADD213SHZm_Intk,\n    VFMADD213SHZm_Intkz,\n    VFMADD213SSZm,\n    VFMADD213SSZm_Int,\n    VFMADD213SSZm_Intk,\n    VFMADD213SSZm_Intkz,\n    VFMADD231PDZ128m,\n    VFMADD231PDZ128mb,\n    VFMADD231PDZ128mbk,\n    VFMADD231PDZ128mbkz,\n    VFMADD231PDZ128mk,\n    VFMADD231PDZ128mkz,\n    VFMADD231PDZ256m,\n    VFMADD231PDZ256mb,\n    VFMADD231PDZ256mbk,\n    VFMADD231PDZ256mbkz,\n    VFMADD231PDZ256mk,\n    VFMADD231PDZ256mkz,\n    VFMADD231PDZm,\n    VFMADD231PDZmb,\n    VFMADD231PDZmbk,\n    VFMADD231PDZmbkz,\n    VFMADD231PDZmk,\n    VFMADD231PDZmkz,\n    VFMADD231PHZ128m,\n    VFMADD231PHZ128mb,\n    VFMADD231PHZ128mbk,\n    VFMADD231PHZ128mbkz,\n    VFMADD231PHZ128mk,\n    VFMADD231PHZ128mkz,\n    VFMADD231PHZ256m,\n    VFMADD231PHZ256mb,\n    VFMADD231PHZ256mbk,\n    VFMADD231PHZ256mbkz,\n    VFMADD231PHZ256mk,\n    VFMADD231PHZ256mkz,\n    VFMADD231PHZm,\n    VFMADD231PHZmb,\n    VFMADD231PHZmbk,\n    VFMADD231PHZmbkz,\n    VFMADD231PHZmk,\n    VFMADD231PHZmkz,\n    VFMADD231PSZ128m,\n    VFMADD231PSZ128mb,\n    VFMADD231PSZ128mbk,\n    VFMADD231PSZ128mbkz,\n    VFMADD231PSZ128mk,\n    VFMADD231PSZ128mkz,\n    VFMADD231PSZ256m,\n    VFMADD231PSZ256mb,\n    VFMADD231PSZ256mbk,\n    VFMADD231PSZ256mbkz,\n    VFMADD231PSZ256mk,\n    VFMADD231PSZ256mkz,\n    VFMADD231PSZm,\n    VFMADD231PSZmb,\n    VFMADD231PSZmbk,\n    VFMADD231PSZmbkz,\n    VFMADD231PSZmk,\n    VFMADD231PSZmkz,\n    VFMADD231SDZm,\n    VFMADD231SDZm_Int,\n    VFMADD231SDZm_Intk,\n    VFMADD231SDZm_Intkz,\n    VFMADD231SHZm,\n    VFMADD231SHZm_Int,\n    VFMADD231SHZm_Intk,\n    VFMADD231SHZm_Intkz,\n    VFMADD231SSZm,\n    VFMADD231SSZm_Int,\n    VFMADD231SSZm_Intk,\n    VFMADD231SSZm_Intkz,\n    VFMADDCPHZ128m,\n    VFMADDCPHZ128mb,\n    VFMADDCPHZ128mbk,\n    VFMADDCPHZ128mbkz,\n    VFMADDCPHZ128mk,\n    VFMADDCPHZ128mkz,\n    VFMADDCPHZ256m,\n    VFMADDCPHZ256mb,\n    VFMADDCPHZ256mbk,\n    VFMADDCPHZ256mbkz,\n    VFMADDCPHZ256mk,\n    VFMADDCPHZ256mkz,\n    VFMADDCPHZm,\n    VFMADDCPHZmb,\n    VFMADDCPHZmbk,\n    VFMADDCPHZmbkz,\n    VFMADDCPHZmk,\n    VFMADDCPHZmkz,\n    VFMADDCSHZm,\n    VFMADDCSHZmk,\n    VFMADDCSHZmkz,\n    VFMADDSUB132PDZ128m,\n    VFMADDSUB132PDZ128mb,\n    VFMADDSUB132PDZ128mbk,\n    VFMADDSUB132PDZ128mbkz,\n    VFMADDSUB132PDZ128mk,\n    VFMADDSUB132PDZ128mkz,\n    VFMADDSUB132PDZ256m,\n    VFMADDSUB132PDZ256mb,\n    VFMADDSUB132PDZ256mbk,\n    VFMADDSUB132PDZ256mbkz,\n    VFMADDSUB132PDZ256mk,\n    VFMADDSUB132PDZ256mkz,\n    VFMADDSUB132PDZm,\n    VFMADDSUB132PDZmb,\n    VFMADDSUB132PDZmbk,\n    VFMADDSUB132PDZmbkz,\n    VFMADDSUB132PDZmk,\n    VFMADDSUB132PDZmkz,\n    VFMADDSUB132PHZ128m,\n    VFMADDSUB132PHZ128mb,\n    VFMADDSUB132PHZ128mbk,\n    VFMADDSUB132PHZ128mbkz,\n    VFMADDSUB132PHZ128mk,\n    VFMADDSUB132PHZ128mkz,\n    VFMADDSUB132PHZ256m,\n    VFMADDSUB132PHZ256mb,\n    VFMADDSUB132PHZ256mbk,\n    VFMADDSUB132PHZ256mbkz,\n    VFMADDSUB132PHZ256mk,\n    VFMADDSUB132PHZ256mkz,\n    VFMADDSUB132PHZm,\n    VFMADDSUB132PHZmb,\n    VFMADDSUB132PHZmbk,\n    VFMADDSUB132PHZmbkz,\n    VFMADDSUB132PHZmk,\n    VFMADDSUB132PHZmkz,\n    VFMADDSUB132PSZ128m,\n    VFMADDSUB132PSZ128mb,\n    VFMADDSUB132PSZ128mbk,\n    VFMADDSUB132PSZ128mbkz,\n    VFMADDSUB132PSZ128mk,\n    VFMADDSUB132PSZ128mkz,\n    VFMADDSUB132PSZ256m,\n    VFMADDSUB132PSZ256mb,\n    VFMADDSUB132PSZ256mbk,\n    VFMADDSUB132PSZ256mbkz,\n    VFMADDSUB132PSZ256mk,\n    VFMADDSUB132PSZ256mkz,\n    VFMADDSUB132PSZm,\n    VFMADDSUB132PSZmb,\n    VFMADDSUB132PSZmbk,\n    VFMADDSUB132PSZmbkz,\n    VFMADDSUB132PSZmk,\n    VFMADDSUB132PSZmkz,\n    VFMADDSUB213PDZ128m,\n    VFMADDSUB213PDZ128mb,\n    VFMADDSUB213PDZ128mbk,\n    VFMADDSUB213PDZ128mbkz,\n    VFMADDSUB213PDZ128mk,\n    VFMADDSUB213PDZ128mkz,\n    VFMADDSUB213PDZ256m,\n    VFMADDSUB213PDZ256mb,\n    VFMADDSUB213PDZ256mbk,\n    VFMADDSUB213PDZ256mbkz,\n    VFMADDSUB213PDZ256mk,\n    VFMADDSUB213PDZ256mkz,\n    VFMADDSUB213PDZm,\n    VFMADDSUB213PDZmb,\n    VFMADDSUB213PDZmbk,\n    VFMADDSUB213PDZmbkz,\n    VFMADDSUB213PDZmk,\n    VFMADDSUB213PDZmkz,\n    VFMADDSUB213PHZ128m,\n    VFMADDSUB213PHZ128mb,\n    VFMADDSUB213PHZ128mbk,\n    VFMADDSUB213PHZ128mbkz,\n    VFMADDSUB213PHZ128mk,\n    VFMADDSUB213PHZ128mkz,\n    VFMADDSUB213PHZ256m,\n    VFMADDSUB213PHZ256mb,\n    VFMADDSUB213PHZ256mbk,\n    VFMADDSUB213PHZ256mbkz,\n    VFMADDSUB213PHZ256mk,\n    VFMADDSUB213PHZ256mkz,\n    VFMADDSUB213PHZm,\n    VFMADDSUB213PHZmb,\n    VFMADDSUB213PHZmbk,\n    VFMADDSUB213PHZmbkz,\n    VFMADDSUB213PHZmk,\n    VFMADDSUB213PHZmkz,\n    VFMADDSUB213PSZ128m,\n    VFMADDSUB213PSZ128mb,\n    VFMADDSUB213PSZ128mbk,\n    VFMADDSUB213PSZ128mbkz,\n    VFMADDSUB213PSZ128mk,\n    VFMADDSUB213PSZ128mkz,\n    VFMADDSUB213PSZ256m,\n    VFMADDSUB213PSZ256mb,\n    VFMADDSUB213PSZ256mbk,\n    VFMADDSUB213PSZ256mbkz,\n    VFMADDSUB213PSZ256mk,\n    VFMADDSUB213PSZ256mkz,\n    VFMADDSUB213PSZm,\n    VFMADDSUB213PSZmb,\n    VFMADDSUB213PSZmbk,\n    VFMADDSUB213PSZmbkz,\n    VFMADDSUB213PSZmk,\n    VFMADDSUB213PSZmkz,\n    VFMADDSUB231PDZ128m,\n    VFMADDSUB231PDZ128mb,\n    VFMADDSUB231PDZ128mbk,\n    VFMADDSUB231PDZ128mbkz,\n    VFMADDSUB231PDZ128mk,\n    VFMADDSUB231PDZ128mkz,\n    VFMADDSUB231PDZ256m,\n    VFMADDSUB231PDZ256mb,\n    VFMADDSUB231PDZ256mbk,\n    VFMADDSUB231PDZ256mbkz,\n    VFMADDSUB231PDZ256mk,\n    VFMADDSUB231PDZ256mkz,\n    VFMADDSUB231PDZm,\n    VFMADDSUB231PDZmb,\n    VFMADDSUB231PDZmbk,\n    VFMADDSUB231PDZmbkz,\n    VFMADDSUB231PDZmk,\n    VFMADDSUB231PDZmkz,\n    VFMADDSUB231PHZ128m,\n    VFMADDSUB231PHZ128mb,\n    VFMADDSUB231PHZ128mbk,\n    VFMADDSUB231PHZ128mbkz,\n    VFMADDSUB231PHZ128mk,\n    VFMADDSUB231PHZ128mkz,\n    VFMADDSUB231PHZ256m,\n    VFMADDSUB231PHZ256mb,\n    VFMADDSUB231PHZ256mbk,\n    VFMADDSUB231PHZ256mbkz,\n    VFMADDSUB231PHZ256mk,\n    VFMADDSUB231PHZ256mkz,\n    VFMADDSUB231PHZm,\n    VFMADDSUB231PHZmb,\n    VFMADDSUB231PHZmbk,\n    VFMADDSUB231PHZmbkz,\n    VFMADDSUB231PHZmk,\n    VFMADDSUB231PHZmkz,\n    VFMADDSUB231PSZ128m,\n    VFMADDSUB231PSZ128mb,\n    VFMADDSUB231PSZ128mbk,\n    VFMADDSUB231PSZ128mbkz,\n    VFMADDSUB231PSZ128mk,\n    VFMADDSUB231PSZ128mkz,\n    VFMADDSUB231PSZ256m,\n    VFMADDSUB231PSZ256mb,\n    VFMADDSUB231PSZ256mbk,\n    VFMADDSUB231PSZ256mbkz,\n    VFMADDSUB231PSZ256mk,\n    VFMADDSUB231PSZ256mkz,\n    VFMADDSUB231PSZm,\n    VFMADDSUB231PSZmb,\n    VFMADDSUB231PSZmbk,\n    VFMADDSUB231PSZmbkz,\n    VFMADDSUB231PSZmk,\n    VFMADDSUB231PSZmkz,\n    VFMSUB132PDZ128m,\n    VFMSUB132PDZ128mb,\n    VFMSUB132PDZ128mbk,\n    VFMSUB132PDZ128mbkz,\n    VFMSUB132PDZ128mk,\n    VFMSUB132PDZ128mkz,\n    VFMSUB132PDZ256m,\n    VFMSUB132PDZ256mb,\n    VFMSUB132PDZ256mbk,\n    VFMSUB132PDZ256mbkz,\n    VFMSUB132PDZ256mk,\n    VFMSUB132PDZ256mkz,\n    VFMSUB132PDZm,\n    VFMSUB132PDZmb,\n    VFMSUB132PDZmbk,\n    VFMSUB132PDZmbkz,\n    VFMSUB132PDZmk,\n    VFMSUB132PDZmkz,\n    VFMSUB132PHZ128m,\n    VFMSUB132PHZ128mb,\n    VFMSUB132PHZ128mbk,\n    VFMSUB132PHZ128mbkz,\n    VFMSUB132PHZ128mk,\n    VFMSUB132PHZ128mkz,\n    VFMSUB132PHZ256m,\n    VFMSUB132PHZ256mb,\n    VFMSUB132PHZ256mbk,\n    VFMSUB132PHZ256mbkz,\n    VFMSUB132PHZ256mk,\n    VFMSUB132PHZ256mkz,\n    VFMSUB132PHZm,\n    VFMSUB132PHZmb,\n    VFMSUB132PHZmbk,\n    VFMSUB132PHZmbkz,\n    VFMSUB132PHZmk,\n    VFMSUB132PHZmkz,\n    VFMSUB132PSZ128m,\n    VFMSUB132PSZ128mb,\n    VFMSUB132PSZ128mbk,\n    VFMSUB132PSZ128mbkz,\n    VFMSUB132PSZ128mk,\n    VFMSUB132PSZ128mkz,\n    VFMSUB132PSZ256m,\n    VFMSUB132PSZ256mb,\n    VFMSUB132PSZ256mbk,\n    VFMSUB132PSZ256mbkz,\n    VFMSUB132PSZ256mk,\n    VFMSUB132PSZ256mkz,\n    VFMSUB132PSZm,\n    VFMSUB132PSZmb,\n    VFMSUB132PSZmbk,\n    VFMSUB132PSZmbkz,\n    VFMSUB132PSZmk,\n    VFMSUB132PSZmkz,\n    VFMSUB132SDZm,\n    VFMSUB132SDZm_Int,\n    VFMSUB132SDZm_Intk,\n    VFMSUB132SDZm_Intkz,\n    VFMSUB132SHZm,\n    VFMSUB132SHZm_Int,\n    VFMSUB132SHZm_Intk,\n    VFMSUB132SHZm_Intkz,\n    VFMSUB132SSZm,\n    VFMSUB132SSZm_Int,\n    VFMSUB132SSZm_Intk,\n    VFMSUB132SSZm_Intkz,\n    VFMSUB213PDZ128m,\n    VFMSUB213PDZ128mb,\n    VFMSUB213PDZ128mbk,\n    VFMSUB213PDZ128mbkz,\n    VFMSUB213PDZ128mk,\n    VFMSUB213PDZ128mkz,\n    VFMSUB213PDZ256m,\n    VFMSUB213PDZ256mb,\n    VFMSUB213PDZ256mbk,\n    VFMSUB213PDZ256mbkz,\n    VFMSUB213PDZ256mk,\n    VFMSUB213PDZ256mkz,\n    VFMSUB213PDZm,\n    VFMSUB213PDZmb,\n    VFMSUB213PDZmbk,\n    VFMSUB213PDZmbkz,\n    VFMSUB213PDZmk,\n    VFMSUB213PDZmkz,\n    VFMSUB213PHZ128m,\n    VFMSUB213PHZ128mb,\n    VFMSUB213PHZ128mbk,\n    VFMSUB213PHZ128mbkz,\n    VFMSUB213PHZ128mk,\n    VFMSUB213PHZ128mkz,\n    VFMSUB213PHZ256m,\n    VFMSUB213PHZ256mb,\n    VFMSUB213PHZ256mbk,\n    VFMSUB213PHZ256mbkz,\n    VFMSUB213PHZ256mk,\n    VFMSUB213PHZ256mkz,\n    VFMSUB213PHZm,\n    VFMSUB213PHZmb,\n    VFMSUB213PHZmbk,\n    VFMSUB213PHZmbkz,\n    VFMSUB213PHZmk,\n    VFMSUB213PHZmkz,\n    VFMSUB213PSZ128m,\n    VFMSUB213PSZ128mb,\n    VFMSUB213PSZ128mbk,\n    VFMSUB213PSZ128mbkz,\n    VFMSUB213PSZ128mk,\n    VFMSUB213PSZ128mkz,\n    VFMSUB213PSZ256m,\n    VFMSUB213PSZ256mb,\n    VFMSUB213PSZ256mbk,\n    VFMSUB213PSZ256mbkz,\n    VFMSUB213PSZ256mk,\n    VFMSUB213PSZ256mkz,\n    VFMSUB213PSZm,\n    VFMSUB213PSZmb,\n    VFMSUB213PSZmbk,\n    VFMSUB213PSZmbkz,\n    VFMSUB213PSZmk,\n    VFMSUB213PSZmkz,\n    VFMSUB213SDZm,\n    VFMSUB213SDZm_Int,\n    VFMSUB213SDZm_Intk,\n    VFMSUB213SDZm_Intkz,\n    VFMSUB213SHZm,\n    VFMSUB213SHZm_Int,\n    VFMSUB213SHZm_Intk,\n    VFMSUB213SHZm_Intkz,\n    VFMSUB213SSZm,\n    VFMSUB213SSZm_Int,\n    VFMSUB213SSZm_Intk,\n    VFMSUB213SSZm_Intkz,\n    VFMSUB231PDZ128m,\n    VFMSUB231PDZ128mb,\n    VFMSUB231PDZ128mbk,\n    VFMSUB231PDZ128mbkz,\n    VFMSUB231PDZ128mk,\n    VFMSUB231PDZ128mkz,\n    VFMSUB231PDZ256m,\n    VFMSUB231PDZ256mb,\n    VFMSUB231PDZ256mbk,\n    VFMSUB231PDZ256mbkz,\n    VFMSUB231PDZ256mk,\n    VFMSUB231PDZ256mkz,\n    VFMSUB231PDZm,\n    VFMSUB231PDZmb,\n    VFMSUB231PDZmbk,\n    VFMSUB231PDZmbkz,\n    VFMSUB231PDZmk,\n    VFMSUB231PDZmkz,\n    VFMSUB231PHZ128m,\n    VFMSUB231PHZ128mb,\n    VFMSUB231PHZ128mbk,\n    VFMSUB231PHZ128mbkz,\n    VFMSUB231PHZ128mk,\n    VFMSUB231PHZ128mkz,\n    VFMSUB231PHZ256m,\n    VFMSUB231PHZ256mb,\n    VFMSUB231PHZ256mbk,\n    VFMSUB231PHZ256mbkz,\n    VFMSUB231PHZ256mk,\n    VFMSUB231PHZ256mkz,\n    VFMSUB231PHZm,\n    VFMSUB231PHZmb,\n    VFMSUB231PHZmbk,\n    VFMSUB231PHZmbkz,\n    VFMSUB231PHZmk,\n    VFMSUB231PHZmkz,\n    VFMSUB231PSZ128m,\n    VFMSUB231PSZ128mb,\n    VFMSUB231PSZ128mbk,\n    VFMSUB231PSZ128mbkz,\n    VFMSUB231PSZ128mk,\n    VFMSUB231PSZ128mkz,\n    VFMSUB231PSZ256m,\n    VFMSUB231PSZ256mb,\n    VFMSUB231PSZ256mbk,\n    VFMSUB231PSZ256mbkz,\n    VFMSUB231PSZ256mk,\n    VFMSUB231PSZ256mkz,\n    VFMSUB231PSZm,\n    VFMSUB231PSZmb,\n    VFMSUB231PSZmbk,\n    VFMSUB231PSZmbkz,\n    VFMSUB231PSZmk,\n    VFMSUB231PSZmkz,\n    VFMSUB231SDZm,\n    VFMSUB231SDZm_Int,\n    VFMSUB231SDZm_Intk,\n    VFMSUB231SDZm_Intkz,\n    VFMSUB231SHZm,\n    VFMSUB231SHZm_Int,\n    VFMSUB231SHZm_Intk,\n    VFMSUB231SHZm_Intkz,\n    VFMSUB231SSZm,\n    VFMSUB231SSZm_Int,\n    VFMSUB231SSZm_Intk,\n    VFMSUB231SSZm_Intkz,\n    VFMSUBADD132PDZ128m,\n    VFMSUBADD132PDZ128mb,\n    VFMSUBADD132PDZ128mbk,\n    VFMSUBADD132PDZ128mbkz,\n    VFMSUBADD132PDZ128mk,\n    VFMSUBADD132PDZ128mkz,\n    VFMSUBADD132PDZ256m,\n    VFMSUBADD132PDZ256mb,\n    VFMSUBADD132PDZ256mbk,\n    VFMSUBADD132PDZ256mbkz,\n    VFMSUBADD132PDZ256mk,\n    VFMSUBADD132PDZ256mkz,\n    VFMSUBADD132PDZm,\n    VFMSUBADD132PDZmb,\n    VFMSUBADD132PDZmbk,\n    VFMSUBADD132PDZmbkz,\n    VFMSUBADD132PDZmk,\n    VFMSUBADD132PDZmkz,\n    VFMSUBADD132PHZ128m,\n    VFMSUBADD132PHZ128mb,\n    VFMSUBADD132PHZ128mbk,\n    VFMSUBADD132PHZ128mbkz,\n    VFMSUBADD132PHZ128mk,\n    VFMSUBADD132PHZ128mkz,\n    VFMSUBADD132PHZ256m,\n    VFMSUBADD132PHZ256mb,\n    VFMSUBADD132PHZ256mbk,\n    VFMSUBADD132PHZ256mbkz,\n    VFMSUBADD132PHZ256mk,\n    VFMSUBADD132PHZ256mkz,\n    VFMSUBADD132PHZm,\n    VFMSUBADD132PHZmb,\n    VFMSUBADD132PHZmbk,\n    VFMSUBADD132PHZmbkz,\n    VFMSUBADD132PHZmk,\n    VFMSUBADD132PHZmkz,\n    VFMSUBADD132PSZ128m,\n    VFMSUBADD132PSZ128mb,\n    VFMSUBADD132PSZ128mbk,\n    VFMSUBADD132PSZ128mbkz,\n    VFMSUBADD132PSZ128mk,\n    VFMSUBADD132PSZ128mkz,\n    VFMSUBADD132PSZ256m,\n    VFMSUBADD132PSZ256mb,\n    VFMSUBADD132PSZ256mbk,\n    VFMSUBADD132PSZ256mbkz,\n    VFMSUBADD132PSZ256mk,\n    VFMSUBADD132PSZ256mkz,\n    VFMSUBADD132PSZm,\n    VFMSUBADD132PSZmb,\n    VFMSUBADD132PSZmbk,\n    VFMSUBADD132PSZmbkz,\n    VFMSUBADD132PSZmk,\n    VFMSUBADD132PSZmkz,\n    VFMSUBADD213PDZ128m,\n    VFMSUBADD213PDZ128mb,\n    VFMSUBADD213PDZ128mbk,\n    VFMSUBADD213PDZ128mbkz,\n    VFMSUBADD213PDZ128mk,\n    VFMSUBADD213PDZ128mkz,\n    VFMSUBADD213PDZ256m,\n    VFMSUBADD213PDZ256mb,\n    VFMSUBADD213PDZ256mbk,\n    VFMSUBADD213PDZ256mbkz,\n    VFMSUBADD213PDZ256mk,\n    VFMSUBADD213PDZ256mkz,\n    VFMSUBADD213PDZm,\n    VFMSUBADD213PDZmb,\n    VFMSUBADD213PDZmbk,\n    VFMSUBADD213PDZmbkz,\n    VFMSUBADD213PDZmk,\n    VFMSUBADD213PDZmkz,\n    VFMSUBADD213PHZ128m,\n    VFMSUBADD213PHZ128mb,\n    VFMSUBADD213PHZ128mbk,\n    VFMSUBADD213PHZ128mbkz,\n    VFMSUBADD213PHZ128mk,\n    VFMSUBADD213PHZ128mkz,\n    VFMSUBADD213PHZ256m,\n    VFMSUBADD213PHZ256mb,\n    VFMSUBADD213PHZ256mbk,\n    VFMSUBADD213PHZ256mbkz,\n    VFMSUBADD213PHZ256mk,\n    VFMSUBADD213PHZ256mkz,\n    VFMSUBADD213PHZm,\n    VFMSUBADD213PHZmb,\n    VFMSUBADD213PHZmbk,\n    VFMSUBADD213PHZmbkz,\n    VFMSUBADD213PHZmk,\n    VFMSUBADD213PHZmkz,\n    VFMSUBADD213PSZ128m,\n    VFMSUBADD213PSZ128mb,\n    VFMSUBADD213PSZ128mbk,\n    VFMSUBADD213PSZ128mbkz,\n    VFMSUBADD213PSZ128mk,\n    VFMSUBADD213PSZ128mkz,\n    VFMSUBADD213PSZ256m,\n    VFMSUBADD213PSZ256mb,\n    VFMSUBADD213PSZ256mbk,\n    VFMSUBADD213PSZ256mbkz,\n    VFMSUBADD213PSZ256mk,\n    VFMSUBADD213PSZ256mkz,\n    VFMSUBADD213PSZm,\n    VFMSUBADD213PSZmb,\n    VFMSUBADD213PSZmbk,\n    VFMSUBADD213PSZmbkz,\n    VFMSUBADD213PSZmk,\n    VFMSUBADD213PSZmkz,\n    VFMSUBADD231PDZ128m,\n    VFMSUBADD231PDZ128mb,\n    VFMSUBADD231PDZ128mbk,\n    VFMSUBADD231PDZ128mbkz,\n    VFMSUBADD231PDZ128mk,\n    VFMSUBADD231PDZ128mkz,\n    VFMSUBADD231PDZ256m,\n    VFMSUBADD231PDZ256mb,\n    VFMSUBADD231PDZ256mbk,\n    VFMSUBADD231PDZ256mbkz,\n    VFMSUBADD231PDZ256mk,\n    VFMSUBADD231PDZ256mkz,\n    VFMSUBADD231PDZm,\n    VFMSUBADD231PDZmb,\n    VFMSUBADD231PDZmbk,\n    VFMSUBADD231PDZmbkz,\n    VFMSUBADD231PDZmk,\n    VFMSUBADD231PDZmkz,\n    VFMSUBADD231PHZ128m,\n    VFMSUBADD231PHZ128mb,\n    VFMSUBADD231PHZ128mbk,\n    VFMSUBADD231PHZ128mbkz,\n    VFMSUBADD231PHZ128mk,\n    VFMSUBADD231PHZ128mkz,\n    VFMSUBADD231PHZ256m,\n    VFMSUBADD231PHZ256mb,\n    VFMSUBADD231PHZ256mbk,\n    VFMSUBADD231PHZ256mbkz,\n    VFMSUBADD231PHZ256mk,\n    VFMSUBADD231PHZ256mkz,\n    VFMSUBADD231PHZm,\n    VFMSUBADD231PHZmb,\n    VFMSUBADD231PHZmbk,\n    VFMSUBADD231PHZmbkz,\n    VFMSUBADD231PHZmk,\n    VFMSUBADD231PHZmkz,\n    VFMSUBADD231PSZ128m,\n    VFMSUBADD231PSZ128mb,\n    VFMSUBADD231PSZ128mbk,\n    VFMSUBADD231PSZ128mbkz,\n    VFMSUBADD231PSZ128mk,\n    VFMSUBADD231PSZ128mkz,\n    VFMSUBADD231PSZ256m,\n    VFMSUBADD231PSZ256mb,\n    VFMSUBADD231PSZ256mbk,\n    VFMSUBADD231PSZ256mbkz,\n    VFMSUBADD231PSZ256mk,\n    VFMSUBADD231PSZ256mkz,\n    VFMSUBADD231PSZm,\n    VFMSUBADD231PSZmb,\n    VFMSUBADD231PSZmbk,\n    VFMSUBADD231PSZmbkz,\n    VFMSUBADD231PSZmk,\n    VFMSUBADD231PSZmkz,\n    VFMULCPHZ128rm,\n    VFMULCPHZ128rmb,\n    VFMULCPHZ128rmbk,\n    VFMULCPHZ128rmbkz,\n    VFMULCPHZ128rmk,\n    VFMULCPHZ128rmkz,\n    VFMULCPHZ256rm,\n    VFMULCPHZ256rmb,\n    VFMULCPHZ256rmbk,\n    VFMULCPHZ256rmbkz,\n    VFMULCPHZ256rmk,\n    VFMULCPHZ256rmkz,\n    VFMULCPHZrm,\n    VFMULCPHZrmb,\n    VFMULCPHZrmbk,\n    VFMULCPHZrmbkz,\n    VFMULCPHZrmk,\n    VFMULCPHZrmkz,\n    VFMULCSHZrm,\n    VFMULCSHZrmk,\n    VFMULCSHZrmkz,\n    VFNMADD132PDZ128m,\n    VFNMADD132PDZ128mb,\n    VFNMADD132PDZ128mbk,\n    VFNMADD132PDZ128mbkz,\n    VFNMADD132PDZ128mk,\n    VFNMADD132PDZ128mkz,\n    VFNMADD132PDZ256m,\n    VFNMADD132PDZ256mb,\n    VFNMADD132PDZ256mbk,\n    VFNMADD132PDZ256mbkz,\n    VFNMADD132PDZ256mk,\n    VFNMADD132PDZ256mkz,\n    VFNMADD132PDZm,\n    VFNMADD132PDZmb,\n    VFNMADD132PDZmbk,\n    VFNMADD132PDZmbkz,\n    VFNMADD132PDZmk,\n    VFNMADD132PDZmkz,\n    VFNMADD132PHZ128m,\n    VFNMADD132PHZ128mb,\n    VFNMADD132PHZ128mbk,\n    VFNMADD132PHZ128mbkz,\n    VFNMADD132PHZ128mk,\n    VFNMADD132PHZ128mkz,\n    VFNMADD132PHZ256m,\n    VFNMADD132PHZ256mb,\n    VFNMADD132PHZ256mbk,\n    VFNMADD132PHZ256mbkz,\n    VFNMADD132PHZ256mk,\n    VFNMADD132PHZ256mkz,\n    VFNMADD132PHZm,\n    VFNMADD132PHZmb,\n    VFNMADD132PHZmbk,\n    VFNMADD132PHZmbkz,\n    VFNMADD132PHZmk,\n    VFNMADD132PHZmkz,\n    VFNMADD132PSZ128m,\n    VFNMADD132PSZ128mb,\n    VFNMADD132PSZ128mbk,\n    VFNMADD132PSZ128mbkz,\n    VFNMADD132PSZ128mk,\n    VFNMADD132PSZ128mkz,\n    VFNMADD132PSZ256m,\n    VFNMADD132PSZ256mb,\n    VFNMADD132PSZ256mbk,\n    VFNMADD132PSZ256mbkz,\n    VFNMADD132PSZ256mk,\n    VFNMADD132PSZ256mkz,\n    VFNMADD132PSZm,\n    VFNMADD132PSZmb,\n    VFNMADD132PSZmbk,\n    VFNMADD132PSZmbkz,\n    VFNMADD132PSZmk,\n    VFNMADD132PSZmkz,\n    VFNMADD132SDZm,\n    VFNMADD132SDZm_Int,\n    VFNMADD132SDZm_Intk,\n    VFNMADD132SDZm_Intkz,\n    VFNMADD132SHZm,\n    VFNMADD132SHZm_Int,\n    VFNMADD132SHZm_Intk,\n    VFNMADD132SHZm_Intkz,\n    VFNMADD132SSZm,\n    VFNMADD132SSZm_Int,\n    VFNMADD132SSZm_Intk,\n    VFNMADD132SSZm_Intkz,\n    VFNMADD213PDZ128m,\n    VFNMADD213PDZ128mb,\n    VFNMADD213PDZ128mbk,\n    VFNMADD213PDZ128mbkz,\n    VFNMADD213PDZ128mk,\n    VFNMADD213PDZ128mkz,\n    VFNMADD213PDZ256m,\n    VFNMADD213PDZ256mb,\n    VFNMADD213PDZ256mbk,\n    VFNMADD213PDZ256mbkz,\n    VFNMADD213PDZ256mk,\n    VFNMADD213PDZ256mkz,\n    VFNMADD213PDZm,\n    VFNMADD213PDZmb,\n    VFNMADD213PDZmbk,\n    VFNMADD213PDZmbkz,\n    VFNMADD213PDZmk,\n    VFNMADD213PDZmkz,\n    VFNMADD213PHZ128m,\n    VFNMADD213PHZ128mb,\n    VFNMADD213PHZ128mbk,\n    VFNMADD213PHZ128mbkz,\n    VFNMADD213PHZ128mk,\n    VFNMADD213PHZ128mkz,\n    VFNMADD213PHZ256m,\n    VFNMADD213PHZ256mb,\n    VFNMADD213PHZ256mbk,\n    VFNMADD213PHZ256mbkz,\n    VFNMADD213PHZ256mk,\n    VFNMADD213PHZ256mkz,\n    VFNMADD213PHZm,\n    VFNMADD213PHZmb,\n    VFNMADD213PHZmbk,\n    VFNMADD213PHZmbkz,\n    VFNMADD213PHZmk,\n    VFNMADD213PHZmkz,\n    VFNMADD213PSZ128m,\n    VFNMADD213PSZ128mb,\n    VFNMADD213PSZ128mbk,\n    VFNMADD213PSZ128mbkz,\n    VFNMADD213PSZ128mk,\n    VFNMADD213PSZ128mkz,\n    VFNMADD213PSZ256m,\n    VFNMADD213PSZ256mb,\n    VFNMADD213PSZ256mbk,\n    VFNMADD213PSZ256mbkz,\n    VFNMADD213PSZ256mk,\n    VFNMADD213PSZ256mkz,\n    VFNMADD213PSZm,\n    VFNMADD213PSZmb,\n    VFNMADD213PSZmbk,\n    VFNMADD213PSZmbkz,\n    VFNMADD213PSZmk,\n    VFNMADD213PSZmkz,\n    VFNMADD213SDZm,\n    VFNMADD213SDZm_Int,\n    VFNMADD213SDZm_Intk,\n    VFNMADD213SDZm_Intkz,\n    VFNMADD213SHZm,\n    VFNMADD213SHZm_Int,\n    VFNMADD213SHZm_Intk,\n    VFNMADD213SHZm_Intkz,\n    VFNMADD213SSZm,\n    VFNMADD213SSZm_Int,\n    VFNMADD213SSZm_Intk,\n    VFNMADD213SSZm_Intkz,\n    VFNMADD231PDZ128m,\n    VFNMADD231PDZ128mb,\n    VFNMADD231PDZ128mbk,\n    VFNMADD231PDZ128mbkz,\n    VFNMADD231PDZ128mk,\n    VFNMADD231PDZ128mkz,\n    VFNMADD231PDZ256m,\n    VFNMADD231PDZ256mb,\n    VFNMADD231PDZ256mbk,\n    VFNMADD231PDZ256mbkz,\n    VFNMADD231PDZ256mk,\n    VFNMADD231PDZ256mkz,\n    VFNMADD231PDZm,\n    VFNMADD231PDZmb,\n    VFNMADD231PDZmbk,\n    VFNMADD231PDZmbkz,\n    VFNMADD231PDZmk,\n    VFNMADD231PDZmkz,\n    VFNMADD231PHZ128m,\n    VFNMADD231PHZ128mb,\n    VFNMADD231PHZ128mbk,\n    VFNMADD231PHZ128mbkz,\n    VFNMADD231PHZ128mk,\n    VFNMADD231PHZ128mkz,\n    VFNMADD231PHZ256m,\n    VFNMADD231PHZ256mb,\n    VFNMADD231PHZ256mbk,\n    VFNMADD231PHZ256mbkz,\n    VFNMADD231PHZ256mk,\n    VFNMADD231PHZ256mkz,\n    VFNMADD231PHZm,\n    VFNMADD231PHZmb,\n    VFNMADD231PHZmbk,\n    VFNMADD231PHZmbkz,\n    VFNMADD231PHZmk,\n    VFNMADD231PHZmkz,\n    VFNMADD231PSZ128m,\n    VFNMADD231PSZ128mb,\n    VFNMADD231PSZ128mbk,\n    VFNMADD231PSZ128mbkz,\n    VFNMADD231PSZ128mk,\n    VFNMADD231PSZ128mkz,\n    VFNMADD231PSZ256m,\n    VFNMADD231PSZ256mb,\n    VFNMADD231PSZ256mbk,\n    VFNMADD231PSZ256mbkz,\n    VFNMADD231PSZ256mk,\n    VFNMADD231PSZ256mkz,\n    VFNMADD231PSZm,\n    VFNMADD231PSZmb,\n    VFNMADD231PSZmbk,\n    VFNMADD231PSZmbkz,\n    VFNMADD231PSZmk,\n    VFNMADD231PSZmkz,\n    VFNMADD231SDZm,\n    VFNMADD231SDZm_Int,\n    VFNMADD231SDZm_Intk,\n    VFNMADD231SDZm_Intkz,\n    VFNMADD231SHZm,\n    VFNMADD231SHZm_Int,\n    VFNMADD231SHZm_Intk,\n    VFNMADD231SHZm_Intkz,\n    VFNMADD231SSZm,\n    VFNMADD231SSZm_Int,\n    VFNMADD231SSZm_Intk,\n    VFNMADD231SSZm_Intkz,\n    VFNMSUB132PDZ128m,\n    VFNMSUB132PDZ128mb,\n    VFNMSUB132PDZ128mbk,\n    VFNMSUB132PDZ128mbkz,\n    VFNMSUB132PDZ128mk,\n    VFNMSUB132PDZ128mkz,\n    VFNMSUB132PDZ256m,\n    VFNMSUB132PDZ256mb,\n    VFNMSUB132PDZ256mbk,\n    VFNMSUB132PDZ256mbkz,\n    VFNMSUB132PDZ256mk,\n    VFNMSUB132PDZ256mkz,\n    VFNMSUB132PDZm,\n    VFNMSUB132PDZmb,\n    VFNMSUB132PDZmbk,\n    VFNMSUB132PDZmbkz,\n    VFNMSUB132PDZmk,\n    VFNMSUB132PDZmkz,\n    VFNMSUB132PHZ128m,\n    VFNMSUB132PHZ128mb,\n    VFNMSUB132PHZ128mbk,\n    VFNMSUB132PHZ128mbkz,\n    VFNMSUB132PHZ128mk,\n    VFNMSUB132PHZ128mkz,\n    VFNMSUB132PHZ256m,\n    VFNMSUB132PHZ256mb,\n    VFNMSUB132PHZ256mbk,\n    VFNMSUB132PHZ256mbkz,\n    VFNMSUB132PHZ256mk,\n    VFNMSUB132PHZ256mkz,\n    VFNMSUB132PHZm,\n    VFNMSUB132PHZmb,\n    VFNMSUB132PHZmbk,\n    VFNMSUB132PHZmbkz,\n    VFNMSUB132PHZmk,\n    VFNMSUB132PHZmkz,\n    VFNMSUB132PSZ128m,\n    VFNMSUB132PSZ128mb,\n    VFNMSUB132PSZ128mbk,\n    VFNMSUB132PSZ128mbkz,\n    VFNMSUB132PSZ128mk,\n    VFNMSUB132PSZ128mkz,\n    VFNMSUB132PSZ256m,\n    VFNMSUB132PSZ256mb,\n    VFNMSUB132PSZ256mbk,\n    VFNMSUB132PSZ256mbkz,\n    VFNMSUB132PSZ256mk,\n    VFNMSUB132PSZ256mkz,\n    VFNMSUB132PSZm,\n    VFNMSUB132PSZmb,\n    VFNMSUB132PSZmbk,\n    VFNMSUB132PSZmbkz,\n    VFNMSUB132PSZmk,\n    VFNMSUB132PSZmkz,\n    VFNMSUB132SDZm,\n    VFNMSUB132SDZm_Int,\n    VFNMSUB132SDZm_Intk,\n    VFNMSUB132SDZm_Intkz,\n    VFNMSUB132SHZm,\n    VFNMSUB132SHZm_Int,\n    VFNMSUB132SHZm_Intk,\n    VFNMSUB132SHZm_Intkz,\n    VFNMSUB132SSZm,\n    VFNMSUB132SSZm_Int,\n    VFNMSUB132SSZm_Intk,\n    VFNMSUB132SSZm_Intkz,\n    VFNMSUB213PDZ128m,\n    VFNMSUB213PDZ128mb,\n    VFNMSUB213PDZ128mbk,\n    VFNMSUB213PDZ128mbkz,\n    VFNMSUB213PDZ128mk,\n    VFNMSUB213PDZ128mkz,\n    VFNMSUB213PDZ256m,\n    VFNMSUB213PDZ256mb,\n    VFNMSUB213PDZ256mbk,\n    VFNMSUB213PDZ256mbkz,\n    VFNMSUB213PDZ256mk,\n    VFNMSUB213PDZ256mkz,\n    VFNMSUB213PDZm,\n    VFNMSUB213PDZmb,\n    VFNMSUB213PDZmbk,\n    VFNMSUB213PDZmbkz,\n    VFNMSUB213PDZmk,\n    VFNMSUB213PDZmkz,\n    VFNMSUB213PHZ128m,\n    VFNMSUB213PHZ128mb,\n    VFNMSUB213PHZ128mbk,\n    VFNMSUB213PHZ128mbkz,\n    VFNMSUB213PHZ128mk,\n    VFNMSUB213PHZ128mkz,\n    VFNMSUB213PHZ256m,\n    VFNMSUB213PHZ256mb,\n    VFNMSUB213PHZ256mbk,\n    VFNMSUB213PHZ256mbkz,\n    VFNMSUB213PHZ256mk,\n    VFNMSUB213PHZ256mkz,\n    VFNMSUB213PHZm,\n    VFNMSUB213PHZmb,\n    VFNMSUB213PHZmbk,\n    VFNMSUB213PHZmbkz,\n    VFNMSUB213PHZmk,\n    VFNMSUB213PHZmkz,\n    VFNMSUB213PSZ128m,\n    VFNMSUB213PSZ128mb,\n    VFNMSUB213PSZ128mbk,\n    VFNMSUB213PSZ128mbkz,\n    VFNMSUB213PSZ128mk,\n    VFNMSUB213PSZ128mkz,\n    VFNMSUB213PSZ256m,\n    VFNMSUB213PSZ256mb,\n    VFNMSUB213PSZ256mbk,\n    VFNMSUB213PSZ256mbkz,\n    VFNMSUB213PSZ256mk,\n    VFNMSUB213PSZ256mkz,\n    VFNMSUB213PSZm,\n    VFNMSUB213PSZmb,\n    VFNMSUB213PSZmbk,\n    VFNMSUB213PSZmbkz,\n    VFNMSUB213PSZmk,\n    VFNMSUB213PSZmkz,\n    VFNMSUB213SDZm,\n    VFNMSUB213SDZm_Int,\n    VFNMSUB213SDZm_Intk,\n    VFNMSUB213SDZm_Intkz,\n    VFNMSUB213SHZm,\n    VFNMSUB213SHZm_Int,\n    VFNMSUB213SHZm_Intk,\n    VFNMSUB213SHZm_Intkz,\n    VFNMSUB213SSZm,\n    VFNMSUB213SSZm_Int,\n    VFNMSUB213SSZm_Intk,\n    VFNMSUB213SSZm_Intkz,\n    VFNMSUB231PDZ128m,\n    VFNMSUB231PDZ128mb,\n    VFNMSUB231PDZ128mbk,\n    VFNMSUB231PDZ128mbkz,\n    VFNMSUB231PDZ128mk,\n    VFNMSUB231PDZ128mkz,\n    VFNMSUB231PDZ256m,\n    VFNMSUB231PDZ256mb,\n    VFNMSUB231PDZ256mbk,\n    VFNMSUB231PDZ256mbkz,\n    VFNMSUB231PDZ256mk,\n    VFNMSUB231PDZ256mkz,\n    VFNMSUB231PDZm,\n    VFNMSUB231PDZmb,\n    VFNMSUB231PDZmbk,\n    VFNMSUB231PDZmbkz,\n    VFNMSUB231PDZmk,\n    VFNMSUB231PDZmkz,\n    VFNMSUB231PHZ128m,\n    VFNMSUB231PHZ128mb,\n    VFNMSUB231PHZ128mbk,\n    VFNMSUB231PHZ128mbkz,\n    VFNMSUB231PHZ128mk,\n    VFNMSUB231PHZ128mkz,\n    VFNMSUB231PHZ256m,\n    VFNMSUB231PHZ256mb,\n    VFNMSUB231PHZ256mbk,\n    VFNMSUB231PHZ256mbkz,\n    VFNMSUB231PHZ256mk,\n    VFNMSUB231PHZ256mkz,\n    VFNMSUB231PHZm,\n    VFNMSUB231PHZmb,\n    VFNMSUB231PHZmbk,\n    VFNMSUB231PHZmbkz,\n    VFNMSUB231PHZmk,\n    VFNMSUB231PHZmkz,\n    VFNMSUB231PSZ128m,\n    VFNMSUB231PSZ128mb,\n    VFNMSUB231PSZ128mbk,\n    VFNMSUB231PSZ128mbkz,\n    VFNMSUB231PSZ128mk,\n    VFNMSUB231PSZ128mkz,\n    VFNMSUB231PSZ256m,\n    VFNMSUB231PSZ256mb,\n    VFNMSUB231PSZ256mbk,\n    VFNMSUB231PSZ256mbkz,\n    VFNMSUB231PSZ256mk,\n    VFNMSUB231PSZ256mkz,\n    VFNMSUB231PSZm,\n    VFNMSUB231PSZmb,\n    VFNMSUB231PSZmbk,\n    VFNMSUB231PSZmbkz,\n    VFNMSUB231PSZmk,\n    VFNMSUB231PSZmkz,\n    VFNMSUB231SDZm,\n    VFNMSUB231SDZm_Int,\n    VFNMSUB231SDZm_Intk,\n    VFNMSUB231SDZm_Intkz,\n    VFNMSUB231SHZm,\n    VFNMSUB231SHZm_Int,\n    VFNMSUB231SHZm_Intk,\n    VFNMSUB231SHZm_Intkz,\n    VFNMSUB231SSZm,\n    VFNMSUB231SSZm_Int,\n    VFNMSUB231SSZm_Intk,\n    VFNMSUB231SSZm_Intkz,\n    VFPCLASSPDZ128rm,\n    VFPCLASSPDZ128rmb,\n    VFPCLASSPDZ128rmbk,\n    VFPCLASSPDZ128rmk,\n    VFPCLASSPDZ256rm,\n    VFPCLASSPDZ256rmb,\n    VFPCLASSPDZ256rmbk,\n    VFPCLASSPDZ256rmk,\n    VFPCLASSPDZrm,\n    VFPCLASSPDZrmb,\n    VFPCLASSPDZrmbk,\n    VFPCLASSPDZrmk,\n    VFPCLASSPHZ128rm,\n    VFPCLASSPHZ128rmb,\n    VFPCLASSPHZ128rmbk,\n    VFPCLASSPHZ128rmk,\n    VFPCLASSPHZ256rm,\n    VFPCLASSPHZ256rmb,\n    VFPCLASSPHZ256rmbk,\n    VFPCLASSPHZ256rmk,\n    VFPCLASSPHZrm,\n    VFPCLASSPHZrmb,\n    VFPCLASSPHZrmbk,\n    VFPCLASSPHZrmk,\n    VFPCLASSPSZ128rm,\n    VFPCLASSPSZ128rmb,\n    VFPCLASSPSZ128rmbk,\n    VFPCLASSPSZ128rmk,\n    VFPCLASSPSZ256rm,\n    VFPCLASSPSZ256rmb,\n    VFPCLASSPSZ256rmbk,\n    VFPCLASSPSZ256rmk,\n    VFPCLASSPSZrm,\n    VFPCLASSPSZrmb,\n    VFPCLASSPSZrmbk,\n    VFPCLASSPSZrmk,\n    VFPCLASSSDZrm,\n    VFPCLASSSDZrmk,\n    VFPCLASSSHZrm,\n    VFPCLASSSHZrmk,\n    VFPCLASSSSZrm,\n    VFPCLASSSSZrmk,\n    VGATHERDPDZ128rm,\n    VGATHERDPDZ256rm,\n    VGATHERDPDZrm,\n    VGATHERDPSZ128rm,\n    VGATHERDPSZ256rm,\n    VGATHERDPSZrm,\n    VGATHERPF0DPDm,\n    VGATHERPF0DPSm,\n    VGATHERPF0QPDm,\n    VGATHERPF0QPSm,\n    VGATHERPF1DPDm,\n    VGATHERPF1DPSm,\n    VGATHERPF1QPDm,\n    VGATHERPF1QPSm,\n    VGATHERQPDZ128rm,\n    VGATHERQPDZ256rm,\n    VGATHERQPDZrm,\n    VGATHERQPSZ128rm,\n    VGATHERQPSZ256rm,\n    VGATHERQPSZrm,\n    VGETEXPPDZ128m,\n    VGETEXPPDZ128mb,\n    VGETEXPPDZ128mbk,\n    VGETEXPPDZ128mbkz,\n    VGETEXPPDZ128mk,\n    VGETEXPPDZ128mkz,\n    VGETEXPPDZ256m,\n    VGETEXPPDZ256mb,\n    VGETEXPPDZ256mbk,\n    VGETEXPPDZ256mbkz,\n    VGETEXPPDZ256mk,\n    VGETEXPPDZ256mkz,\n    VGETEXPPDZm,\n    VGETEXPPDZmb,\n    VGETEXPPDZmbk,\n    VGETEXPPDZmbkz,\n    VGETEXPPDZmk,\n    VGETEXPPDZmkz,\n    VGETEXPPHZ128m,\n    VGETEXPPHZ128mb,\n    VGETEXPPHZ128mbk,\n    VGETEXPPHZ128mbkz,\n    VGETEXPPHZ128mk,\n    VGETEXPPHZ128mkz,\n    VGETEXPPHZ256m,\n    VGETEXPPHZ256mb,\n    VGETEXPPHZ256mbk,\n    VGETEXPPHZ256mbkz,\n    VGETEXPPHZ256mk,\n    VGETEXPPHZ256mkz,\n    VGETEXPPHZm,\n    VGETEXPPHZmb,\n    VGETEXPPHZmbk,\n    VGETEXPPHZmbkz,\n    VGETEXPPHZmk,\n    VGETEXPPHZmkz,\n    VGETEXPPSZ128m,\n    VGETEXPPSZ128mb,\n    VGETEXPPSZ128mbk,\n    VGETEXPPSZ128mbkz,\n    VGETEXPPSZ128mk,\n    VGETEXPPSZ128mkz,\n    VGETEXPPSZ256m,\n    VGETEXPPSZ256mb,\n    VGETEXPPSZ256mbk,\n    VGETEXPPSZ256mbkz,\n    VGETEXPPSZ256mk,\n    VGETEXPPSZ256mkz,\n    VGETEXPPSZm,\n    VGETEXPPSZmb,\n    VGETEXPPSZmbk,\n    VGETEXPPSZmbkz,\n    VGETEXPPSZmk,\n    VGETEXPPSZmkz,\n    VGETEXPSDZm,\n    VGETEXPSDZmk,\n    VGETEXPSDZmkz,\n    VGETEXPSHZm,\n    VGETEXPSHZmk,\n    VGETEXPSHZmkz,\n    VGETEXPSSZm,\n    VGETEXPSSZmk,\n    VGETEXPSSZmkz,\n    VGETMANTPDZ128rmbi,\n    VGETMANTPDZ128rmbik,\n    VGETMANTPDZ128rmbikz,\n    VGETMANTPDZ128rmi,\n    VGETMANTPDZ128rmik,\n    VGETMANTPDZ128rmikz,\n    VGETMANTPDZ256rmbi,\n    VGETMANTPDZ256rmbik,\n    VGETMANTPDZ256rmbikz,\n    VGETMANTPDZ256rmi,\n    VGETMANTPDZ256rmik,\n    VGETMANTPDZ256rmikz,\n    VGETMANTPDZrmbi,\n    VGETMANTPDZrmbik,\n    VGETMANTPDZrmbikz,\n    VGETMANTPDZrmi,\n    VGETMANTPDZrmik,\n    VGETMANTPDZrmikz,\n    VGETMANTPHZ128rmbi,\n    VGETMANTPHZ128rmbik,\n    VGETMANTPHZ128rmbikz,\n    VGETMANTPHZ128rmi,\n    VGETMANTPHZ128rmik,\n    VGETMANTPHZ128rmikz,\n    VGETMANTPHZ256rmbi,\n    VGETMANTPHZ256rmbik,\n    VGETMANTPHZ256rmbikz,\n    VGETMANTPHZ256rmi,\n    VGETMANTPHZ256rmik,\n    VGETMANTPHZ256rmikz,\n    VGETMANTPHZrmbi,\n    VGETMANTPHZrmbik,\n    VGETMANTPHZrmbikz,\n    VGETMANTPHZrmi,\n    VGETMANTPHZrmik,\n    VGETMANTPHZrmikz,\n    VGETMANTPSZ128rmbi,\n    VGETMANTPSZ128rmbik,\n    VGETMANTPSZ128rmbikz,\n    VGETMANTPSZ128rmi,\n    VGETMANTPSZ128rmik,\n    VGETMANTPSZ128rmikz,\n    VGETMANTPSZ256rmbi,\n    VGETMANTPSZ256rmbik,\n    VGETMANTPSZ256rmbikz,\n    VGETMANTPSZ256rmi,\n    VGETMANTPSZ256rmik,\n    VGETMANTPSZ256rmikz,\n    VGETMANTPSZrmbi,\n    VGETMANTPSZrmbik,\n    VGETMANTPSZrmbikz,\n    VGETMANTPSZrmi,\n    VGETMANTPSZrmik,\n    VGETMANTPSZrmikz,\n    VGETMANTSDZrmi,\n    VGETMANTSDZrmik,\n    VGETMANTSDZrmikz,\n    VGETMANTSHZrmi,\n    VGETMANTSHZrmik,\n    VGETMANTSHZrmikz,\n    VGETMANTSSZrmi,\n    VGETMANTSSZrmik,\n    VGETMANTSSZrmikz,\n    VGF2P8AFFINEINVQBZ128rmbi,\n    VGF2P8AFFINEINVQBZ128rmbik,\n    VGF2P8AFFINEINVQBZ128rmbikz,\n    VGF2P8AFFINEINVQBZ128rmi,\n    VGF2P8AFFINEINVQBZ128rmik,\n    VGF2P8AFFINEINVQBZ128rmikz,\n    VGF2P8AFFINEINVQBZ256rmbi,\n    VGF2P8AFFINEINVQBZ256rmbik,\n    VGF2P8AFFINEINVQBZ256rmbikz,\n    VGF2P8AFFINEINVQBZ256rmi,\n    VGF2P8AFFINEINVQBZ256rmik,\n    VGF2P8AFFINEINVQBZ256rmikz,\n    VGF2P8AFFINEINVQBZrmbi,\n    VGF2P8AFFINEINVQBZrmbik,\n    VGF2P8AFFINEINVQBZrmbikz,\n    VGF2P8AFFINEINVQBZrmi,\n    VGF2P8AFFINEINVQBZrmik,\n    VGF2P8AFFINEINVQBZrmikz,\n    VGF2P8AFFINEQBZ128rmbi,\n    VGF2P8AFFINEQBZ128rmbik,\n    VGF2P8AFFINEQBZ128rmbikz,\n    VGF2P8AFFINEQBZ128rmi,\n    VGF2P8AFFINEQBZ128rmik,\n    VGF2P8AFFINEQBZ128rmikz,\n    VGF2P8AFFINEQBZ256rmbi,\n    VGF2P8AFFINEQBZ256rmbik,\n    VGF2P8AFFINEQBZ256rmbikz,\n    VGF2P8AFFINEQBZ256rmi,\n    VGF2P8AFFINEQBZ256rmik,\n    VGF2P8AFFINEQBZ256rmikz,\n    VGF2P8AFFINEQBZrmbi,\n    VGF2P8AFFINEQBZrmbik,\n    VGF2P8AFFINEQBZrmbikz,\n    VGF2P8AFFINEQBZrmi,\n    VGF2P8AFFINEQBZrmik,\n    VGF2P8AFFINEQBZrmikz,\n    VGF2P8MULBZ128rm,\n    VGF2P8MULBZ128rmk,\n    VGF2P8MULBZ128rmkz,\n    VGF2P8MULBZ256rm,\n    VGF2P8MULBZ256rmk,\n    VGF2P8MULBZ256rmkz,\n    VGF2P8MULBZrm,\n    VGF2P8MULBZrmk,\n    VGF2P8MULBZrmkz,\n    VINSERTF32x4Z256rm,\n    VINSERTF32x4Z256rmk,\n    VINSERTF32x4Z256rmkz,\n    VINSERTF32x4Zrm,\n    VINSERTF32x4Zrmk,\n    VINSERTF32x4Zrmkz,\n    VINSERTF32x8Zrm,\n    VINSERTF32x8Zrmk,\n    VINSERTF32x8Zrmkz,\n    VINSERTF64x2Z256rm,\n    VINSERTF64x2Z256rmk,\n    VINSERTF64x2Z256rmkz,\n    VINSERTF64x2Zrm,\n    VINSERTF64x2Zrmk,\n    VINSERTF64x2Zrmkz,\n    VINSERTF64x4Zrm,\n    VINSERTF64x4Zrmk,\n    VINSERTF64x4Zrmkz,\n    VINSERTI32x4Z256rm,\n    VINSERTI32x4Z256rmk,\n    VINSERTI32x4Z256rmkz,\n    VINSERTI32x4Zrm,\n    VINSERTI32x4Zrmk,\n    VINSERTI32x4Zrmkz,\n    VINSERTI32x8Zrm,\n    VINSERTI32x8Zrmk,\n    VINSERTI32x8Zrmkz,\n    VINSERTI64x2Z256rm,\n    VINSERTI64x2Z256rmk,\n    VINSERTI64x2Z256rmkz,\n    VINSERTI64x2Zrm,\n    VINSERTI64x2Zrmk,\n    VINSERTI64x2Zrmkz,\n    VINSERTI64x4Zrm,\n    VINSERTI64x4Zrmk,\n    VINSERTI64x4Zrmkz,\n    VINSERTPSZrm,\n    VMAXCPDZ128rm,\n    VMAXCPDZ128rmb,\n    VMAXCPDZ128rmbk,\n    VMAXCPDZ128rmbkz,\n    VMAXCPDZ128rmk,\n    VMAXCPDZ128rmkz,\n    VMAXCPDZ256rm,\n    VMAXCPDZ256rmb,\n    VMAXCPDZ256rmbk,\n    VMAXCPDZ256rmbkz,\n    VMAXCPDZ256rmk,\n    VMAXCPDZ256rmkz,\n    VMAXCPDZrm,\n    VMAXCPDZrmb,\n    VMAXCPDZrmbk,\n    VMAXCPDZrmbkz,\n    VMAXCPDZrmk,\n    VMAXCPDZrmkz,\n    VMAXCPHZ128rm,\n    VMAXCPHZ128rmb,\n    VMAXCPHZ128rmbk,\n    VMAXCPHZ128rmbkz,\n    VMAXCPHZ128rmk,\n    VMAXCPHZ128rmkz,\n    VMAXCPHZ256rm,\n    VMAXCPHZ256rmb,\n    VMAXCPHZ256rmbk,\n    VMAXCPHZ256rmbkz,\n    VMAXCPHZ256rmk,\n    VMAXCPHZ256rmkz,\n    VMAXCPHZrm,\n    VMAXCPHZrmb,\n    VMAXCPHZrmbk,\n    VMAXCPHZrmbkz,\n    VMAXCPHZrmk,\n    VMAXCPHZrmkz,\n    VMAXCPSZ128rm,\n    VMAXCPSZ128rmb,\n    VMAXCPSZ128rmbk,\n    VMAXCPSZ128rmbkz,\n    VMAXCPSZ128rmk,\n    VMAXCPSZ128rmkz,\n    VMAXCPSZ256rm,\n    VMAXCPSZ256rmb,\n    VMAXCPSZ256rmbk,\n    VMAXCPSZ256rmbkz,\n    VMAXCPSZ256rmk,\n    VMAXCPSZ256rmkz,\n    VMAXCPSZrm,\n    VMAXCPSZrmb,\n    VMAXCPSZrmbk,\n    VMAXCPSZrmbkz,\n    VMAXCPSZrmk,\n    VMAXCPSZrmkz,\n    VMAXPDZ128rm,\n    VMAXPDZ128rmb,\n    VMAXPDZ128rmbk,\n    VMAXPDZ128rmbkz,\n    VMAXPDZ128rmk,\n    VMAXPDZ128rmkz,\n    VMAXPDZ256rm,\n    VMAXPDZ256rmb,\n    VMAXPDZ256rmbk,\n    VMAXPDZ256rmbkz,\n    VMAXPDZ256rmk,\n    VMAXPDZ256rmkz,\n    VMAXPDZrm,\n    VMAXPDZrmb,\n    VMAXPDZrmbk,\n    VMAXPDZrmbkz,\n    VMAXPDZrmk,\n    VMAXPDZrmkz,\n    VMAXPHZ128rm,\n    VMAXPHZ128rmb,\n    VMAXPHZ128rmbk,\n    VMAXPHZ128rmbkz,\n    VMAXPHZ128rmk,\n    VMAXPHZ128rmkz,\n    VMAXPHZ256rm,\n    VMAXPHZ256rmb,\n    VMAXPHZ256rmbk,\n    VMAXPHZ256rmbkz,\n    VMAXPHZ256rmk,\n    VMAXPHZ256rmkz,\n    VMAXPHZrm,\n    VMAXPHZrmb,\n    VMAXPHZrmbk,\n    VMAXPHZrmbkz,\n    VMAXPHZrmk,\n    VMAXPHZrmkz,\n    VMAXPSZ128rm,\n    VMAXPSZ128rmb,\n    VMAXPSZ128rmbk,\n    VMAXPSZ128rmbkz,\n    VMAXPSZ128rmk,\n    VMAXPSZ128rmkz,\n    VMAXPSZ256rm,\n    VMAXPSZ256rmb,\n    VMAXPSZ256rmbk,\n    VMAXPSZ256rmbkz,\n    VMAXPSZ256rmk,\n    VMAXPSZ256rmkz,\n    VMAXPSZrm,\n    VMAXPSZrmb,\n    VMAXPSZrmbk,\n    VMAXPSZrmbkz,\n    VMAXPSZrmk,\n    VMAXPSZrmkz,\n    VMAXSDZrm_Int,\n    VMAXSDZrm_Intk,\n    VMAXSDZrm_Intkz,\n    VMAXSHZrm_Int,\n    VMAXSHZrm_Intk,\n    VMAXSHZrm_Intkz,\n    VMAXSSZrm_Int,\n    VMAXSSZrm_Intk,\n    VMAXSSZrm_Intkz,\n    VMINCPDZ128rm,\n    VMINCPDZ128rmb,\n    VMINCPDZ128rmbk,\n    VMINCPDZ128rmbkz,\n    VMINCPDZ128rmk,\n    VMINCPDZ128rmkz,\n    VMINCPDZ256rm,\n    VMINCPDZ256rmb,\n    VMINCPDZ256rmbk,\n    VMINCPDZ256rmbkz,\n    VMINCPDZ256rmk,\n    VMINCPDZ256rmkz,\n    VMINCPDZrm,\n    VMINCPDZrmb,\n    VMINCPDZrmbk,\n    VMINCPDZrmbkz,\n    VMINCPDZrmk,\n    VMINCPDZrmkz,\n    VMINCPHZ128rm,\n    VMINCPHZ128rmb,\n    VMINCPHZ128rmbk,\n    VMINCPHZ128rmbkz,\n    VMINCPHZ128rmk,\n    VMINCPHZ128rmkz,\n    VMINCPHZ256rm,\n    VMINCPHZ256rmb,\n    VMINCPHZ256rmbk,\n    VMINCPHZ256rmbkz,\n    VMINCPHZ256rmk,\n    VMINCPHZ256rmkz,\n    VMINCPHZrm,\n    VMINCPHZrmb,\n    VMINCPHZrmbk,\n    VMINCPHZrmbkz,\n    VMINCPHZrmk,\n    VMINCPHZrmkz,\n    VMINCPSZ128rm,\n    VMINCPSZ128rmb,\n    VMINCPSZ128rmbk,\n    VMINCPSZ128rmbkz,\n    VMINCPSZ128rmk,\n    VMINCPSZ128rmkz,\n    VMINCPSZ256rm,\n    VMINCPSZ256rmb,\n    VMINCPSZ256rmbk,\n    VMINCPSZ256rmbkz,\n    VMINCPSZ256rmk,\n    VMINCPSZ256rmkz,\n    VMINCPSZrm,\n    VMINCPSZrmb,\n    VMINCPSZrmbk,\n    VMINCPSZrmbkz,\n    VMINCPSZrmk,\n    VMINCPSZrmkz,\n    VMINPDZ128rm,\n    VMINPDZ128rmb,\n    VMINPDZ128rmbk,\n    VMINPDZ128rmbkz,\n    VMINPDZ128rmk,\n    VMINPDZ128rmkz,\n    VMINPDZ256rm,\n    VMINPDZ256rmb,\n    VMINPDZ256rmbk,\n    VMINPDZ256rmbkz,\n    VMINPDZ256rmk,\n    VMINPDZ256rmkz,\n    VMINPDZrm,\n    VMINPDZrmb,\n    VMINPDZrmbk,\n    VMINPDZrmbkz,\n    VMINPDZrmk,\n    VMINPDZrmkz,\n    VMINPHZ128rm,\n    VMINPHZ128rmb,\n    VMINPHZ128rmbk,\n    VMINPHZ128rmbkz,\n    VMINPHZ128rmk,\n    VMINPHZ128rmkz,\n    VMINPHZ256rm,\n    VMINPHZ256rmb,\n    VMINPHZ256rmbk,\n    VMINPHZ256rmbkz,\n    VMINPHZ256rmk,\n    VMINPHZ256rmkz,\n    VMINPHZrm,\n    VMINPHZrmb,\n    VMINPHZrmbk,\n    VMINPHZrmbkz,\n    VMINPHZrmk,\n    VMINPHZrmkz,\n    VMINPSZ128rm,\n    VMINPSZ128rmb,\n    VMINPSZ128rmbk,\n    VMINPSZ128rmbkz,\n    VMINPSZ128rmk,\n    VMINPSZ128rmkz,\n    VMINPSZ256rm,\n    VMINPSZ256rmb,\n    VMINPSZ256rmbk,\n    VMINPSZ256rmbkz,\n    VMINPSZ256rmk,\n    VMINPSZ256rmkz,\n    VMINPSZrm,\n    VMINPSZrmb,\n    VMINPSZrmbk,\n    VMINPSZrmbkz,\n    VMINPSZrmk,\n    VMINPSZrmkz,\n    VMINSDZrm_Int,\n    VMINSDZrm_Intk,\n    VMINSDZrm_Intkz,\n    VMINSHZrm_Int,\n    VMINSHZrm_Intk,\n    VMINSHZrm_Intkz,\n    VMINSSZrm_Int,\n    VMINSSZrm_Intk,\n    VMINSSZrm_Intkz,\n    VMOV64toPQIZrm,\n    VMOVAPDZ128mr,\n    VMOVAPDZ128mrk,\n    VMOVAPDZ128rm,\n    VMOVAPDZ128rmk,\n    VMOVAPDZ128rmkz,\n    VMOVAPDZ256mr,\n    VMOVAPDZ256mrk,\n    VMOVAPDZ256rm,\n    VMOVAPDZ256rmk,\n    VMOVAPDZ256rmkz,\n    VMOVAPDZmr,\n    VMOVAPDZmrk,\n    VMOVAPDZrm,\n    VMOVAPDZrmk,\n    VMOVAPDZrmkz,\n    VMOVAPSZ128mr,\n    VMOVAPSZ128mrk,\n    VMOVAPSZ128rm,\n    VMOVAPSZ128rmk,\n    VMOVAPSZ128rmkz,\n    VMOVAPSZ256mr,\n    VMOVAPSZ256mrk,\n    VMOVAPSZ256rm,\n    VMOVAPSZ256rmk,\n    VMOVAPSZ256rmkz,\n    VMOVAPSZmr,\n    VMOVAPSZmrk,\n    VMOVAPSZrm,\n    VMOVAPSZrmk,\n    VMOVAPSZrmkz,\n    VMOVDDUPZ128rm,\n    VMOVDDUPZ128rmk,\n    VMOVDDUPZ128rmkz,\n    VMOVDDUPZ256rm,\n    VMOVDDUPZ256rmk,\n    VMOVDDUPZ256rmkz,\n    VMOVDDUPZrm,\n    VMOVDDUPZrmk,\n    VMOVDDUPZrmkz,\n    VMOVDI2PDIZrm,\n    VMOVDQA32Z128mr,\n    VMOVDQA32Z128mrk,\n    VMOVDQA32Z128rm,\n    VMOVDQA32Z128rmk,\n    VMOVDQA32Z128rmkz,\n    VMOVDQA32Z256mr,\n    VMOVDQA32Z256mrk,\n    VMOVDQA32Z256rm,\n    VMOVDQA32Z256rmk,\n    VMOVDQA32Z256rmkz,\n    VMOVDQA32Zmr,\n    VMOVDQA32Zmrk,\n    VMOVDQA32Zrm,\n    VMOVDQA32Zrmk,\n    VMOVDQA32Zrmkz,\n    VMOVDQA64Z128mr,\n    VMOVDQA64Z128mrk,\n    VMOVDQA64Z128rm,\n    VMOVDQA64Z128rmk,\n    VMOVDQA64Z128rmkz,\n    VMOVDQA64Z256mr,\n    VMOVDQA64Z256mrk,\n    VMOVDQA64Z256rm,\n    VMOVDQA64Z256rmk,\n    VMOVDQA64Z256rmkz,\n    VMOVDQA64Zmr,\n    VMOVDQA64Zmrk,\n    VMOVDQA64Zrm,\n    VMOVDQA64Zrmk,\n    VMOVDQA64Zrmkz,\n    VMOVDQU16Z128mr,\n    VMOVDQU16Z128mrk,\n    VMOVDQU16Z128rm,\n    VMOVDQU16Z128rmk,\n    VMOVDQU16Z128rmkz,\n    VMOVDQU16Z256mr,\n    VMOVDQU16Z256mrk,\n    VMOVDQU16Z256rm,\n    VMOVDQU16Z256rmk,\n    VMOVDQU16Z256rmkz,\n    VMOVDQU16Zmr,\n    VMOVDQU16Zmrk,\n    VMOVDQU16Zrm,\n    VMOVDQU16Zrmk,\n    VMOVDQU16Zrmkz,\n    VMOVDQU32Z128mr,\n    VMOVDQU32Z128mrk,\n    VMOVDQU32Z128rm,\n    VMOVDQU32Z128rmk,\n    VMOVDQU32Z128rmkz,\n    VMOVDQU32Z256mr,\n    VMOVDQU32Z256mrk,\n    VMOVDQU32Z256rm,\n    VMOVDQU32Z256rmk,\n    VMOVDQU32Z256rmkz,\n    VMOVDQU32Zmr,\n    VMOVDQU32Zmrk,\n    VMOVDQU32Zrm,\n    VMOVDQU32Zrmk,\n    VMOVDQU32Zrmkz,\n    VMOVDQU64Z128mr,\n    VMOVDQU64Z128mrk,\n    VMOVDQU64Z128rm,\n    VMOVDQU64Z128rmk,\n    VMOVDQU64Z128rmkz,\n    VMOVDQU64Z256mr,\n    VMOVDQU64Z256mrk,\n    VMOVDQU64Z256rm,\n    VMOVDQU64Z256rmk,\n    VMOVDQU64Z256rmkz,\n    VMOVDQU64Zmr,\n    VMOVDQU64Zmrk,\n    VMOVDQU64Zrm,\n    VMOVDQU64Zrmk,\n    VMOVDQU64Zrmkz,\n    VMOVDQU8Z128mr,\n    VMOVDQU8Z128mrk,\n    VMOVDQU8Z128rm,\n    VMOVDQU8Z128rmk,\n    VMOVDQU8Z128rmkz,\n    VMOVDQU8Z256mr,\n    VMOVDQU8Z256mrk,\n    VMOVDQU8Z256rm,\n    VMOVDQU8Z256rmk,\n    VMOVDQU8Z256rmkz,\n    VMOVDQU8Zmr,\n    VMOVDQU8Zmrk,\n    VMOVDQU8Zrm,\n    VMOVDQU8Zrmk,\n    VMOVDQU8Zrmkz,\n    VMOVHPDZ128mr,\n    VMOVHPDZ128rm,\n    VMOVHPSZ128mr,\n    VMOVHPSZ128rm,\n    VMOVLPDZ128mr,\n    VMOVLPDZ128rm,\n    VMOVLPSZ128mr,\n    VMOVLPSZ128rm,\n    VMOVNTDQAZ128rm,\n    VMOVNTDQAZ256rm,\n    VMOVNTDQAZrm,\n    VMOVNTDQZ128mr,\n    VMOVNTDQZ256mr,\n    VMOVNTDQZmr,\n    VMOVNTPDZ128mr,\n    VMOVNTPDZ256mr,\n    VMOVNTPDZmr,\n    VMOVNTPSZ128mr,\n    VMOVNTPSZ256mr,\n    VMOVNTPSZmr,\n    VMOVPDI2DIZmr,\n    VMOVQI2PQIZrm,\n    VMOVSDZmr,\n    VMOVSDZmrk,\n    VMOVSDZrm,\n    VMOVSDZrm_alt,\n    VMOVSDZrmk,\n    VMOVSDZrmkz,\n    VMOVSHDUPZ128rm,\n    VMOVSHDUPZ128rmk,\n    VMOVSHDUPZ128rmkz,\n    VMOVSHDUPZ256rm,\n    VMOVSHDUPZ256rmk,\n    VMOVSHDUPZ256rmkz,\n    VMOVSHDUPZrm,\n    VMOVSHDUPZrmk,\n    VMOVSHDUPZrmkz,\n    VMOVSHZmr,\n    VMOVSHZmrk,\n    VMOVSHZrm,\n    VMOVSHZrm_alt,\n    VMOVSHZrmk,\n    VMOVSHZrmkz,\n    VMOVSLDUPZ128rm,\n    VMOVSLDUPZ128rmk,\n    VMOVSLDUPZ128rmkz,\n    VMOVSLDUPZ256rm,\n    VMOVSLDUPZ256rmk,\n    VMOVSLDUPZ256rmkz,\n    VMOVSLDUPZrm,\n    VMOVSLDUPZrmk,\n    VMOVSLDUPZrmkz,\n    VMOVSSZmr,\n    VMOVSSZmrk,\n    VMOVSSZrm,\n    VMOVSSZrm_alt,\n    VMOVSSZrmk,\n    VMOVSSZrmkz,\n    VMOVUPDZ128mr,\n    VMOVUPDZ128mrk,\n    VMOVUPDZ128rm,\n    VMOVUPDZ128rmk,\n    VMOVUPDZ128rmkz,\n    VMOVUPDZ256mr,\n    VMOVUPDZ256mrk,\n    VMOVUPDZ256rm,\n    VMOVUPDZ256rmk,\n    VMOVUPDZ256rmkz,\n    VMOVUPDZmr,\n    VMOVUPDZmrk,\n    VMOVUPDZrm,\n    VMOVUPDZrmk,\n    VMOVUPDZrmkz,\n    VMOVUPSZ128mr,\n    VMOVUPSZ128mrk,\n    VMOVUPSZ128rm,\n    VMOVUPSZ128rmk,\n    VMOVUPSZ128rmkz,\n    VMOVUPSZ256mr,\n    VMOVUPSZ256mrk,\n    VMOVUPSZ256rm,\n    VMOVUPSZ256rmk,\n    VMOVUPSZ256rmkz,\n    VMOVUPSZmr,\n    VMOVUPSZmrk,\n    VMOVUPSZrm,\n    VMOVUPSZrmk,\n    VMOVUPSZrmkz,\n    VMOVWmr,\n    VMOVWrm,\n    VMULPDZ128rm,\n    VMULPDZ128rmb,\n    VMULPDZ128rmbk,\n    VMULPDZ128rmbkz,\n    VMULPDZ128rmk,\n    VMULPDZ128rmkz,\n    VMULPDZ256rm,\n    VMULPDZ256rmb,\n    VMULPDZ256rmbk,\n    VMULPDZ256rmbkz,\n    VMULPDZ256rmk,\n    VMULPDZ256rmkz,\n    VMULPDZrm,\n    VMULPDZrmb,\n    VMULPDZrmbk,\n    VMULPDZrmbkz,\n    VMULPDZrmk,\n    VMULPDZrmkz,\n    VMULPHZ128rm,\n    VMULPHZ128rmb,\n    VMULPHZ128rmbk,\n    VMULPHZ128rmbkz,\n    VMULPHZ128rmk,\n    VMULPHZ128rmkz,\n    VMULPHZ256rm,\n    VMULPHZ256rmb,\n    VMULPHZ256rmbk,\n    VMULPHZ256rmbkz,\n    VMULPHZ256rmk,\n    VMULPHZ256rmkz,\n    VMULPHZrm,\n    VMULPHZrmb,\n    VMULPHZrmbk,\n    VMULPHZrmbkz,\n    VMULPHZrmk,\n    VMULPHZrmkz,\n    VMULPSZ128rm,\n    VMULPSZ128rmb,\n    VMULPSZ128rmbk,\n    VMULPSZ128rmbkz,\n    VMULPSZ128rmk,\n    VMULPSZ128rmkz,\n    VMULPSZ256rm,\n    VMULPSZ256rmb,\n    VMULPSZ256rmbk,\n    VMULPSZ256rmbkz,\n    VMULPSZ256rmk,\n    VMULPSZ256rmkz,\n    VMULPSZrm,\n    VMULPSZrmb,\n    VMULPSZrmbk,\n    VMULPSZrmbkz,\n    VMULPSZrmk,\n    VMULPSZrmkz,\n    VMULSDZrm_Int,\n    VMULSDZrm_Intk,\n    VMULSDZrm_Intkz,\n    VMULSHZrm_Int,\n    VMULSHZrm_Intk,\n    VMULSHZrm_Intkz,\n    VMULSSZrm_Int,\n    VMULSSZrm_Intk,\n    VMULSSZrm_Intkz,\n    VORPDZ128rm,\n    VORPDZ128rmb,\n    VORPDZ128rmbk,\n    VORPDZ128rmbkz,\n    VORPDZ128rmk,\n    VORPDZ128rmkz,\n    VORPDZ256rm,\n    VORPDZ256rmb,\n    VORPDZ256rmbk,\n    VORPDZ256rmbkz,\n    VORPDZ256rmk,\n    VORPDZ256rmkz,\n    VORPDZrm,\n    VORPDZrmb,\n    VORPDZrmbk,\n    VORPDZrmbkz,\n    VORPDZrmk,\n    VORPDZrmkz,\n    VORPSZ128rm,\n    VORPSZ128rmb,\n    VORPSZ128rmbk,\n    VORPSZ128rmbkz,\n    VORPSZ128rmk,\n    VORPSZ128rmkz,\n    VORPSZ256rm,\n    VORPSZ256rmb,\n    VORPSZ256rmbk,\n    VORPSZ256rmbkz,\n    VORPSZ256rmk,\n    VORPSZ256rmkz,\n    VORPSZrm,\n    VORPSZrmb,\n    VORPSZrmbk,\n    VORPSZrmbkz,\n    VORPSZrmk,\n    VORPSZrmkz,\n    VP4DPWSSDSrm,\n    VP4DPWSSDSrmk,\n    VP4DPWSSDSrmkz,\n    VP4DPWSSDrm,\n    VP4DPWSSDrmk,\n    VP4DPWSSDrmkz,\n    VPABSBZ128rm,\n    VPABSBZ128rmk,\n    VPABSBZ128rmkz,\n    VPABSBZ256rm,\n    VPABSBZ256rmk,\n    VPABSBZ256rmkz,\n    VPABSBZrm,\n    VPABSBZrmk,\n    VPABSBZrmkz,\n    VPABSDZ128rm,\n    VPABSDZ128rmb,\n    VPABSDZ128rmbk,\n    VPABSDZ128rmbkz,\n    VPABSDZ128rmk,\n    VPABSDZ128rmkz,\n    VPABSDZ256rm,\n    VPABSDZ256rmb,\n    VPABSDZ256rmbk,\n    VPABSDZ256rmbkz,\n    VPABSDZ256rmk,\n    VPABSDZ256rmkz,\n    VPABSDZrm,\n    VPABSDZrmb,\n    VPABSDZrmbk,\n    VPABSDZrmbkz,\n    VPABSDZrmk,\n    VPABSDZrmkz,\n    VPABSQZ128rm,\n    VPABSQZ128rmb,\n    VPABSQZ128rmbk,\n    VPABSQZ128rmbkz,\n    VPABSQZ128rmk,\n    VPABSQZ128rmkz,\n    VPABSQZ256rm,\n    VPABSQZ256rmb,\n    VPABSQZ256rmbk,\n    VPABSQZ256rmbkz,\n    VPABSQZ256rmk,\n    VPABSQZ256rmkz,\n    VPABSQZrm,\n    VPABSQZrmb,\n    VPABSQZrmbk,\n    VPABSQZrmbkz,\n    VPABSQZrmk,\n    VPABSQZrmkz,\n    VPABSWZ128rm,\n    VPABSWZ128rmk,\n    VPABSWZ128rmkz,\n    VPABSWZ256rm,\n    VPABSWZ256rmk,\n    VPABSWZ256rmkz,\n    VPABSWZrm,\n    VPABSWZrmk,\n    VPABSWZrmkz,\n    VPACKSSDWZ128rm,\n    VPACKSSDWZ128rmb,\n    VPACKSSDWZ128rmbk,\n    VPACKSSDWZ128rmbkz,\n    VPACKSSDWZ128rmk,\n    VPACKSSDWZ128rmkz,\n    VPACKSSDWZ256rm,\n    VPACKSSDWZ256rmb,\n    VPACKSSDWZ256rmbk,\n    VPACKSSDWZ256rmbkz,\n    VPACKSSDWZ256rmk,\n    VPACKSSDWZ256rmkz,\n    VPACKSSDWZrm,\n    VPACKSSDWZrmb,\n    VPACKSSDWZrmbk,\n    VPACKSSDWZrmbkz,\n    VPACKSSDWZrmk,\n    VPACKSSDWZrmkz,\n    VPACKSSWBZ128rm,\n    VPACKSSWBZ128rmk,\n    VPACKSSWBZ128rmkz,\n    VPACKSSWBZ256rm,\n    VPACKSSWBZ256rmk,\n    VPACKSSWBZ256rmkz,\n    VPACKSSWBZrm,\n    VPACKSSWBZrmk,\n    VPACKSSWBZrmkz,\n    VPACKUSDWZ128rm,\n    VPACKUSDWZ128rmb,\n    VPACKUSDWZ128rmbk,\n    VPACKUSDWZ128rmbkz,\n    VPACKUSDWZ128rmk,\n    VPACKUSDWZ128rmkz,\n    VPACKUSDWZ256rm,\n    VPACKUSDWZ256rmb,\n    VPACKUSDWZ256rmbk,\n    VPACKUSDWZ256rmbkz,\n    VPACKUSDWZ256rmk,\n    VPACKUSDWZ256rmkz,\n    VPACKUSDWZrm,\n    VPACKUSDWZrmb,\n    VPACKUSDWZrmbk,\n    VPACKUSDWZrmbkz,\n    VPACKUSDWZrmk,\n    VPACKUSDWZrmkz,\n    VPACKUSWBZ128rm,\n    VPACKUSWBZ128rmk,\n    VPACKUSWBZ128rmkz,\n    VPACKUSWBZ256rm,\n    VPACKUSWBZ256rmk,\n    VPACKUSWBZ256rmkz,\n    VPACKUSWBZrm,\n    VPACKUSWBZrmk,\n    VPACKUSWBZrmkz,\n    VPADDBZ128rm,\n    VPADDBZ128rmk,\n    VPADDBZ128rmkz,\n    VPADDBZ256rm,\n    VPADDBZ256rmk,\n    VPADDBZ256rmkz,\n    VPADDBZrm,\n    VPADDBZrmk,\n    VPADDBZrmkz,\n    VPADDDZ128rm,\n    VPADDDZ128rmb,\n    VPADDDZ128rmbk,\n    VPADDDZ128rmbkz,\n    VPADDDZ128rmk,\n    VPADDDZ128rmkz,\n    VPADDDZ256rm,\n    VPADDDZ256rmb,\n    VPADDDZ256rmbk,\n    VPADDDZ256rmbkz,\n    VPADDDZ256rmk,\n    VPADDDZ256rmkz,\n    VPADDDZrm,\n    VPADDDZrmb,\n    VPADDDZrmbk,\n    VPADDDZrmbkz,\n    VPADDDZrmk,\n    VPADDDZrmkz,\n    VPADDQZ128rm,\n    VPADDQZ128rmb,\n    VPADDQZ128rmbk,\n    VPADDQZ128rmbkz,\n    VPADDQZ128rmk,\n    VPADDQZ128rmkz,\n    VPADDQZ256rm,\n    VPADDQZ256rmb,\n    VPADDQZ256rmbk,\n    VPADDQZ256rmbkz,\n    VPADDQZ256rmk,\n    VPADDQZ256rmkz,\n    VPADDQZrm,\n    VPADDQZrmb,\n    VPADDQZrmbk,\n    VPADDQZrmbkz,\n    VPADDQZrmk,\n    VPADDQZrmkz,\n    VPADDSBZ128rm,\n    VPADDSBZ128rmk,\n    VPADDSBZ128rmkz,\n    VPADDSBZ256rm,\n    VPADDSBZ256rmk,\n    VPADDSBZ256rmkz,\n    VPADDSBZrm,\n    VPADDSBZrmk,\n    VPADDSBZrmkz,\n    VPADDSWZ128rm,\n    VPADDSWZ128rmk,\n    VPADDSWZ128rmkz,\n    VPADDSWZ256rm,\n    VPADDSWZ256rmk,\n    VPADDSWZ256rmkz,\n    VPADDSWZrm,\n    VPADDSWZrmk,\n    VPADDSWZrmkz,\n    VPADDUSBZ128rm,\n    VPADDUSBZ128rmk,\n    VPADDUSBZ128rmkz,\n    VPADDUSBZ256rm,\n    VPADDUSBZ256rmk,\n    VPADDUSBZ256rmkz,\n    VPADDUSBZrm,\n    VPADDUSBZrmk,\n    VPADDUSBZrmkz,\n    VPADDUSWZ128rm,\n    VPADDUSWZ128rmk,\n    VPADDUSWZ128rmkz,\n    VPADDUSWZ256rm,\n    VPADDUSWZ256rmk,\n    VPADDUSWZ256rmkz,\n    VPADDUSWZrm,\n    VPADDUSWZrmk,\n    VPADDUSWZrmkz,\n    VPADDWZ128rm,\n    VPADDWZ128rmk,\n    VPADDWZ128rmkz,\n    VPADDWZ256rm,\n    VPADDWZ256rmk,\n    VPADDWZ256rmkz,\n    VPADDWZrm,\n    VPADDWZrmk,\n    VPADDWZrmkz,\n    VPALIGNRZ128rmi,\n    VPALIGNRZ128rmik,\n    VPALIGNRZ128rmikz,\n    VPALIGNRZ256rmi,\n    VPALIGNRZ256rmik,\n    VPALIGNRZ256rmikz,\n    VPALIGNRZrmi,\n    VPALIGNRZrmik,\n    VPALIGNRZrmikz,\n    VPANDDZ128rm,\n    VPANDDZ128rmb,\n    VPANDDZ128rmbk,\n    VPANDDZ128rmbkz,\n    VPANDDZ128rmk,\n    VPANDDZ128rmkz,\n    VPANDDZ256rm,\n    VPANDDZ256rmb,\n    VPANDDZ256rmbk,\n    VPANDDZ256rmbkz,\n    VPANDDZ256rmk,\n    VPANDDZ256rmkz,\n    VPANDDZrm,\n    VPANDDZrmb,\n    VPANDDZrmbk,\n    VPANDDZrmbkz,\n    VPANDDZrmk,\n    VPANDDZrmkz,\n    VPANDNDZ128rm,\n    VPANDNDZ128rmb,\n    VPANDNDZ128rmbk,\n    VPANDNDZ128rmbkz,\n    VPANDNDZ128rmk,\n    VPANDNDZ128rmkz,\n    VPANDNDZ256rm,\n    VPANDNDZ256rmb,\n    VPANDNDZ256rmbk,\n    VPANDNDZ256rmbkz,\n    VPANDNDZ256rmk,\n    VPANDNDZ256rmkz,\n    VPANDNDZrm,\n    VPANDNDZrmb,\n    VPANDNDZrmbk,\n    VPANDNDZrmbkz,\n    VPANDNDZrmk,\n    VPANDNDZrmkz,\n    VPANDNQZ128rm,\n    VPANDNQZ128rmb,\n    VPANDNQZ128rmbk,\n    VPANDNQZ128rmbkz,\n    VPANDNQZ128rmk,\n    VPANDNQZ128rmkz,\n    VPANDNQZ256rm,\n    VPANDNQZ256rmb,\n    VPANDNQZ256rmbk,\n    VPANDNQZ256rmbkz,\n    VPANDNQZ256rmk,\n    VPANDNQZ256rmkz,\n    VPANDNQZrm,\n    VPANDNQZrmb,\n    VPANDNQZrmbk,\n    VPANDNQZrmbkz,\n    VPANDNQZrmk,\n    VPANDNQZrmkz,\n    VPANDQZ128rm,\n    VPANDQZ128rmb,\n    VPANDQZ128rmbk,\n    VPANDQZ128rmbkz,\n    VPANDQZ128rmk,\n    VPANDQZ128rmkz,\n    VPANDQZ256rm,\n    VPANDQZ256rmb,\n    VPANDQZ256rmbk,\n    VPANDQZ256rmbkz,\n    VPANDQZ256rmk,\n    VPANDQZ256rmkz,\n    VPANDQZrm,\n    VPANDQZrmb,\n    VPANDQZrmbk,\n    VPANDQZrmbkz,\n    VPANDQZrmk,\n    VPANDQZrmkz,\n    VPAVGBZ128rm,\n    VPAVGBZ128rmk,\n    VPAVGBZ128rmkz,\n    VPAVGBZ256rm,\n    VPAVGBZ256rmk,\n    VPAVGBZ256rmkz,\n    VPAVGBZrm,\n    VPAVGBZrmk,\n    VPAVGBZrmkz,\n    VPAVGWZ128rm,\n    VPAVGWZ128rmk,\n    VPAVGWZ128rmkz,\n    VPAVGWZ256rm,\n    VPAVGWZ256rmk,\n    VPAVGWZ256rmkz,\n    VPAVGWZrm,\n    VPAVGWZrmk,\n    VPAVGWZrmkz,\n    VPBLENDMBZ128rm,\n    VPBLENDMBZ128rmk,\n    VPBLENDMBZ128rmkz,\n    VPBLENDMBZ256rm,\n    VPBLENDMBZ256rmk,\n    VPBLENDMBZ256rmkz,\n    VPBLENDMBZrm,\n    VPBLENDMBZrmk,\n    VPBLENDMBZrmkz,\n    VPBLENDMDZ128rm,\n    VPBLENDMDZ128rmb,\n    VPBLENDMDZ128rmbk,\n    VPBLENDMDZ128rmbkz,\n    VPBLENDMDZ128rmk,\n    VPBLENDMDZ128rmkz,\n    VPBLENDMDZ256rm,\n    VPBLENDMDZ256rmb,\n    VPBLENDMDZ256rmbk,\n    VPBLENDMDZ256rmbkz,\n    VPBLENDMDZ256rmk,\n    VPBLENDMDZ256rmkz,\n    VPBLENDMDZrm,\n    VPBLENDMDZrmb,\n    VPBLENDMDZrmbk,\n    VPBLENDMDZrmbkz,\n    VPBLENDMDZrmk,\n    VPBLENDMDZrmkz,\n    VPBLENDMQZ128rm,\n    VPBLENDMQZ128rmb,\n    VPBLENDMQZ128rmbk,\n    VPBLENDMQZ128rmbkz,\n    VPBLENDMQZ128rmk,\n    VPBLENDMQZ128rmkz,\n    VPBLENDMQZ256rm,\n    VPBLENDMQZ256rmb,\n    VPBLENDMQZ256rmbk,\n    VPBLENDMQZ256rmbkz,\n    VPBLENDMQZ256rmk,\n    VPBLENDMQZ256rmkz,\n    VPBLENDMQZrm,\n    VPBLENDMQZrmb,\n    VPBLENDMQZrmbk,\n    VPBLENDMQZrmbkz,\n    VPBLENDMQZrmk,\n    VPBLENDMQZrmkz,\n    VPBLENDMWZ128rm,\n    VPBLENDMWZ128rmk,\n    VPBLENDMWZ128rmkz,\n    VPBLENDMWZ256rm,\n    VPBLENDMWZ256rmk,\n    VPBLENDMWZ256rmkz,\n    VPBLENDMWZrm,\n    VPBLENDMWZrmk,\n    VPBLENDMWZrmkz,\n    VPBROADCASTBZ128rm,\n    VPBROADCASTBZ128rmk,\n    VPBROADCASTBZ128rmkz,\n    VPBROADCASTBZ256rm,\n    VPBROADCASTBZ256rmk,\n    VPBROADCASTBZ256rmkz,\n    VPBROADCASTBZrm,\n    VPBROADCASTBZrmk,\n    VPBROADCASTBZrmkz,\n    VPBROADCASTDZ128rm,\n    VPBROADCASTDZ128rmk,\n    VPBROADCASTDZ128rmkz,\n    VPBROADCASTDZ256rm,\n    VPBROADCASTDZ256rmk,\n    VPBROADCASTDZ256rmkz,\n    VPBROADCASTDZrm,\n    VPBROADCASTDZrmk,\n    VPBROADCASTDZrmkz,\n    VPBROADCASTQZ128rm,\n    VPBROADCASTQZ128rmk,\n    VPBROADCASTQZ128rmkz,\n    VPBROADCASTQZ256rm,\n    VPBROADCASTQZ256rmk,\n    VPBROADCASTQZ256rmkz,\n    VPBROADCASTQZrm,\n    VPBROADCASTQZrmk,\n    VPBROADCASTQZrmkz,\n    VPBROADCASTWZ128rm,\n    VPBROADCASTWZ128rmk,\n    VPBROADCASTWZ128rmkz,\n    VPBROADCASTWZ256rm,\n    VPBROADCASTWZ256rmk,\n    VPBROADCASTWZ256rmkz,\n    VPBROADCASTWZrm,\n    VPBROADCASTWZrmk,\n    VPBROADCASTWZrmkz,\n    VPCMPBZ128rmi,\n    VPCMPBZ128rmik,\n    VPCMPBZ256rmi,\n    VPCMPBZ256rmik,\n    VPCMPBZrmi,\n    VPCMPBZrmik,\n    VPCMPDZ128rmi,\n    VPCMPDZ128rmib,\n    VPCMPDZ128rmibk,\n    VPCMPDZ128rmik,\n    VPCMPDZ256rmi,\n    VPCMPDZ256rmib,\n    VPCMPDZ256rmibk,\n    VPCMPDZ256rmik,\n    VPCMPDZrmi,\n    VPCMPDZrmib,\n    VPCMPDZrmibk,\n    VPCMPDZrmik,\n    VPCMPEQBZ128rm,\n    VPCMPEQBZ128rmk,\n    VPCMPEQBZ256rm,\n    VPCMPEQBZ256rmk,\n    VPCMPEQBZrm,\n    VPCMPEQBZrmk,\n    VPCMPEQDZ128rm,\n    VPCMPEQDZ128rmb,\n    VPCMPEQDZ128rmbk,\n    VPCMPEQDZ128rmk,\n    VPCMPEQDZ256rm,\n    VPCMPEQDZ256rmb,\n    VPCMPEQDZ256rmbk,\n    VPCMPEQDZ256rmk,\n    VPCMPEQDZrm,\n    VPCMPEQDZrmb,\n    VPCMPEQDZrmbk,\n    VPCMPEQDZrmk,\n    VPCMPEQQZ128rm,\n    VPCMPEQQZ128rmb,\n    VPCMPEQQZ128rmbk,\n    VPCMPEQQZ128rmk,\n    VPCMPEQQZ256rm,\n    VPCMPEQQZ256rmb,\n    VPCMPEQQZ256rmbk,\n    VPCMPEQQZ256rmk,\n    VPCMPEQQZrm,\n    VPCMPEQQZrmb,\n    VPCMPEQQZrmbk,\n    VPCMPEQQZrmk,\n    VPCMPEQWZ128rm,\n    VPCMPEQWZ128rmk,\n    VPCMPEQWZ256rm,\n    VPCMPEQWZ256rmk,\n    VPCMPEQWZrm,\n    VPCMPEQWZrmk,\n    VPCMPGTBZ128rm,\n    VPCMPGTBZ128rmk,\n    VPCMPGTBZ256rm,\n    VPCMPGTBZ256rmk,\n    VPCMPGTBZrm,\n    VPCMPGTBZrmk,\n    VPCMPGTDZ128rm,\n    VPCMPGTDZ128rmb,\n    VPCMPGTDZ128rmbk,\n    VPCMPGTDZ128rmk,\n    VPCMPGTDZ256rm,\n    VPCMPGTDZ256rmb,\n    VPCMPGTDZ256rmbk,\n    VPCMPGTDZ256rmk,\n    VPCMPGTDZrm,\n    VPCMPGTDZrmb,\n    VPCMPGTDZrmbk,\n    VPCMPGTDZrmk,\n    VPCMPGTQZ128rm,\n    VPCMPGTQZ128rmb,\n    VPCMPGTQZ128rmbk,\n    VPCMPGTQZ128rmk,\n    VPCMPGTQZ256rm,\n    VPCMPGTQZ256rmb,\n    VPCMPGTQZ256rmbk,\n    VPCMPGTQZ256rmk,\n    VPCMPGTQZrm,\n    VPCMPGTQZrmb,\n    VPCMPGTQZrmbk,\n    VPCMPGTQZrmk,\n    VPCMPGTWZ128rm,\n    VPCMPGTWZ128rmk,\n    VPCMPGTWZ256rm,\n    VPCMPGTWZ256rmk,\n    VPCMPGTWZrm,\n    VPCMPGTWZrmk,\n    VPCMPQZ128rmi,\n    VPCMPQZ128rmib,\n    VPCMPQZ128rmibk,\n    VPCMPQZ128rmik,\n    VPCMPQZ256rmi,\n    VPCMPQZ256rmib,\n    VPCMPQZ256rmibk,\n    VPCMPQZ256rmik,\n    VPCMPQZrmi,\n    VPCMPQZrmib,\n    VPCMPQZrmibk,\n    VPCMPQZrmik,\n    VPCMPUBZ128rmi,\n    VPCMPUBZ128rmik,\n    VPCMPUBZ256rmi,\n    VPCMPUBZ256rmik,\n    VPCMPUBZrmi,\n    VPCMPUBZrmik,\n    VPCMPUDZ128rmi,\n    VPCMPUDZ128rmib,\n    VPCMPUDZ128rmibk,\n    VPCMPUDZ128rmik,\n    VPCMPUDZ256rmi,\n    VPCMPUDZ256rmib,\n    VPCMPUDZ256rmibk,\n    VPCMPUDZ256rmik,\n    VPCMPUDZrmi,\n    VPCMPUDZrmib,\n    VPCMPUDZrmibk,\n    VPCMPUDZrmik,\n    VPCMPUQZ128rmi,\n    VPCMPUQZ128rmib,\n    VPCMPUQZ128rmibk,\n    VPCMPUQZ128rmik,\n    VPCMPUQZ256rmi,\n    VPCMPUQZ256rmib,\n    VPCMPUQZ256rmibk,\n    VPCMPUQZ256rmik,\n    VPCMPUQZrmi,\n    VPCMPUQZrmib,\n    VPCMPUQZrmibk,\n    VPCMPUQZrmik,\n    VPCMPUWZ128rmi,\n    VPCMPUWZ128rmik,\n    VPCMPUWZ256rmi,\n    VPCMPUWZ256rmik,\n    VPCMPUWZrmi,\n    VPCMPUWZrmik,\n    VPCMPWZ128rmi,\n    VPCMPWZ128rmik,\n    VPCMPWZ256rmi,\n    VPCMPWZ256rmik,\n    VPCMPWZrmi,\n    VPCMPWZrmik,\n    VPCOMPRESSBZ128mr,\n    VPCOMPRESSBZ128mrk,\n    VPCOMPRESSBZ256mr,\n    VPCOMPRESSBZ256mrk,\n    VPCOMPRESSBZmr,\n    VPCOMPRESSBZmrk,\n    VPCOMPRESSDZ128mr,\n    VPCOMPRESSDZ128mrk,\n    VPCOMPRESSDZ256mr,\n    VPCOMPRESSDZ256mrk,\n    VPCOMPRESSDZmr,\n    VPCOMPRESSDZmrk,\n    VPCOMPRESSQZ128mr,\n    VPCOMPRESSQZ128mrk,\n    VPCOMPRESSQZ256mr,\n    VPCOMPRESSQZ256mrk,\n    VPCOMPRESSQZmr,\n    VPCOMPRESSQZmrk,\n    VPCOMPRESSWZ128mr,\n    VPCOMPRESSWZ128mrk,\n    VPCOMPRESSWZ256mr,\n    VPCOMPRESSWZ256mrk,\n    VPCOMPRESSWZmr,\n    VPCOMPRESSWZmrk,\n    VPCONFLICTDZ128rm,\n    VPCONFLICTDZ128rmb,\n    VPCONFLICTDZ128rmbk,\n    VPCONFLICTDZ128rmbkz,\n    VPCONFLICTDZ128rmk,\n    VPCONFLICTDZ128rmkz,\n    VPCONFLICTDZ256rm,\n    VPCONFLICTDZ256rmb,\n    VPCONFLICTDZ256rmbk,\n    VPCONFLICTDZ256rmbkz,\n    VPCONFLICTDZ256rmk,\n    VPCONFLICTDZ256rmkz,\n    VPCONFLICTDZrm,\n    VPCONFLICTDZrmb,\n    VPCONFLICTDZrmbk,\n    VPCONFLICTDZrmbkz,\n    VPCONFLICTDZrmk,\n    VPCONFLICTDZrmkz,\n    VPCONFLICTQZ128rm,\n    VPCONFLICTQZ128rmb,\n    VPCONFLICTQZ128rmbk,\n    VPCONFLICTQZ128rmbkz,\n    VPCONFLICTQZ128rmk,\n    VPCONFLICTQZ128rmkz,\n    VPCONFLICTQZ256rm,\n    VPCONFLICTQZ256rmb,\n    VPCONFLICTQZ256rmbk,\n    VPCONFLICTQZ256rmbkz,\n    VPCONFLICTQZ256rmk,\n    VPCONFLICTQZ256rmkz,\n    VPCONFLICTQZrm,\n    VPCONFLICTQZrmb,\n    VPCONFLICTQZrmbk,\n    VPCONFLICTQZrmbkz,\n    VPCONFLICTQZrmk,\n    VPCONFLICTQZrmkz,\n    VPDPBUSDSZ128m,\n    VPDPBUSDSZ128mb,\n    VPDPBUSDSZ128mbk,\n    VPDPBUSDSZ128mbkz,\n    VPDPBUSDSZ128mk,\n    VPDPBUSDSZ128mkz,\n    VPDPBUSDSZ256m,\n    VPDPBUSDSZ256mb,\n    VPDPBUSDSZ256mbk,\n    VPDPBUSDSZ256mbkz,\n    VPDPBUSDSZ256mk,\n    VPDPBUSDSZ256mkz,\n    VPDPBUSDSZm,\n    VPDPBUSDSZmb,\n    VPDPBUSDSZmbk,\n    VPDPBUSDSZmbkz,\n    VPDPBUSDSZmk,\n    VPDPBUSDSZmkz,\n    VPDPBUSDZ128m,\n    VPDPBUSDZ128mb,\n    VPDPBUSDZ128mbk,\n    VPDPBUSDZ128mbkz,\n    VPDPBUSDZ128mk,\n    VPDPBUSDZ128mkz,\n    VPDPBUSDZ256m,\n    VPDPBUSDZ256mb,\n    VPDPBUSDZ256mbk,\n    VPDPBUSDZ256mbkz,\n    VPDPBUSDZ256mk,\n    VPDPBUSDZ256mkz,\n    VPDPBUSDZm,\n    VPDPBUSDZmb,\n    VPDPBUSDZmbk,\n    VPDPBUSDZmbkz,\n    VPDPBUSDZmk,\n    VPDPBUSDZmkz,\n    VPDPWSSDSZ128m,\n    VPDPWSSDSZ128mb,\n    VPDPWSSDSZ128mbk,\n    VPDPWSSDSZ128mbkz,\n    VPDPWSSDSZ128mk,\n    VPDPWSSDSZ128mkz,\n    VPDPWSSDSZ256m,\n    VPDPWSSDSZ256mb,\n    VPDPWSSDSZ256mbk,\n    VPDPWSSDSZ256mbkz,\n    VPDPWSSDSZ256mk,\n    VPDPWSSDSZ256mkz,\n    VPDPWSSDSZm,\n    VPDPWSSDSZmb,\n    VPDPWSSDSZmbk,\n    VPDPWSSDSZmbkz,\n    VPDPWSSDSZmk,\n    VPDPWSSDSZmkz,\n    VPDPWSSDZ128m,\n    VPDPWSSDZ128mb,\n    VPDPWSSDZ128mbk,\n    VPDPWSSDZ128mbkz,\n    VPDPWSSDZ128mk,\n    VPDPWSSDZ128mkz,\n    VPDPWSSDZ256m,\n    VPDPWSSDZ256mb,\n    VPDPWSSDZ256mbk,\n    VPDPWSSDZ256mbkz,\n    VPDPWSSDZ256mk,\n    VPDPWSSDZ256mkz,\n    VPDPWSSDZm,\n    VPDPWSSDZmb,\n    VPDPWSSDZmbk,\n    VPDPWSSDZmbkz,\n    VPDPWSSDZmk,\n    VPDPWSSDZmkz,\n    VPERMBZ128rm,\n    VPERMBZ128rmk,\n    VPERMBZ128rmkz,\n    VPERMBZ256rm,\n    VPERMBZ256rmk,\n    VPERMBZ256rmkz,\n    VPERMBZrm,\n    VPERMBZrmk,\n    VPERMBZrmkz,\n    VPERMDZ256rm,\n    VPERMDZ256rmb,\n    VPERMDZ256rmbk,\n    VPERMDZ256rmbkz,\n    VPERMDZ256rmk,\n    VPERMDZ256rmkz,\n    VPERMDZrm,\n    VPERMDZrmb,\n    VPERMDZrmbk,\n    VPERMDZrmbkz,\n    VPERMDZrmk,\n    VPERMDZrmkz,\n    VPERMI2BZ128rm,\n    VPERMI2BZ128rmk,\n    VPERMI2BZ128rmkz,\n    VPERMI2BZ256rm,\n    VPERMI2BZ256rmk,\n    VPERMI2BZ256rmkz,\n    VPERMI2BZrm,\n    VPERMI2BZrmk,\n    VPERMI2BZrmkz,\n    VPERMI2DZ128rm,\n    VPERMI2DZ128rmb,\n    VPERMI2DZ128rmbk,\n    VPERMI2DZ128rmbkz,\n    VPERMI2DZ128rmk,\n    VPERMI2DZ128rmkz,\n    VPERMI2DZ256rm,\n    VPERMI2DZ256rmb,\n    VPERMI2DZ256rmbk,\n    VPERMI2DZ256rmbkz,\n    VPERMI2DZ256rmk,\n    VPERMI2DZ256rmkz,\n    VPERMI2DZrm,\n    VPERMI2DZrmb,\n    VPERMI2DZrmbk,\n    VPERMI2DZrmbkz,\n    VPERMI2DZrmk,\n    VPERMI2DZrmkz,\n    VPERMI2PDZ128rm,\n    VPERMI2PDZ128rmb,\n    VPERMI2PDZ128rmbk,\n    VPERMI2PDZ128rmbkz,\n    VPERMI2PDZ128rmk,\n    VPERMI2PDZ128rmkz,\n    VPERMI2PDZ256rm,\n    VPERMI2PDZ256rmb,\n    VPERMI2PDZ256rmbk,\n    VPERMI2PDZ256rmbkz,\n    VPERMI2PDZ256rmk,\n    VPERMI2PDZ256rmkz,\n    VPERMI2PDZrm,\n    VPERMI2PDZrmb,\n    VPERMI2PDZrmbk,\n    VPERMI2PDZrmbkz,\n    VPERMI2PDZrmk,\n    VPERMI2PDZrmkz,\n    VPERMI2PSZ128rm,\n    VPERMI2PSZ128rmb,\n    VPERMI2PSZ128rmbk,\n    VPERMI2PSZ128rmbkz,\n    VPERMI2PSZ128rmk,\n    VPERMI2PSZ128rmkz,\n    VPERMI2PSZ256rm,\n    VPERMI2PSZ256rmb,\n    VPERMI2PSZ256rmbk,\n    VPERMI2PSZ256rmbkz,\n    VPERMI2PSZ256rmk,\n    VPERMI2PSZ256rmkz,\n    VPERMI2PSZrm,\n    VPERMI2PSZrmb,\n    VPERMI2PSZrmbk,\n    VPERMI2PSZrmbkz,\n    VPERMI2PSZrmk,\n    VPERMI2PSZrmkz,\n    VPERMI2QZ128rm,\n    VPERMI2QZ128rmb,\n    VPERMI2QZ128rmbk,\n    VPERMI2QZ128rmbkz,\n    VPERMI2QZ128rmk,\n    VPERMI2QZ128rmkz,\n    VPERMI2QZ256rm,\n    VPERMI2QZ256rmb,\n    VPERMI2QZ256rmbk,\n    VPERMI2QZ256rmbkz,\n    VPERMI2QZ256rmk,\n    VPERMI2QZ256rmkz,\n    VPERMI2QZrm,\n    VPERMI2QZrmb,\n    VPERMI2QZrmbk,\n    VPERMI2QZrmbkz,\n    VPERMI2QZrmk,\n    VPERMI2QZrmkz,\n    VPERMI2WZ128rm,\n    VPERMI2WZ128rmk,\n    VPERMI2WZ128rmkz,\n    VPERMI2WZ256rm,\n    VPERMI2WZ256rmk,\n    VPERMI2WZ256rmkz,\n    VPERMI2WZrm,\n    VPERMI2WZrmk,\n    VPERMI2WZrmkz,\n    VPERMILPDZ128mbi,\n    VPERMILPDZ128mbik,\n    VPERMILPDZ128mbikz,\n    VPERMILPDZ128mi,\n    VPERMILPDZ128mik,\n    VPERMILPDZ128mikz,\n    VPERMILPDZ128rm,\n    VPERMILPDZ128rmb,\n    VPERMILPDZ128rmbk,\n    VPERMILPDZ128rmbkz,\n    VPERMILPDZ128rmk,\n    VPERMILPDZ128rmkz,\n    VPERMILPDZ256mbi,\n    VPERMILPDZ256mbik,\n    VPERMILPDZ256mbikz,\n    VPERMILPDZ256mi,\n    VPERMILPDZ256mik,\n    VPERMILPDZ256mikz,\n    VPERMILPDZ256rm,\n    VPERMILPDZ256rmb,\n    VPERMILPDZ256rmbk,\n    VPERMILPDZ256rmbkz,\n    VPERMILPDZ256rmk,\n    VPERMILPDZ256rmkz,\n    VPERMILPDZmbi,\n    VPERMILPDZmbik,\n    VPERMILPDZmbikz,\n    VPERMILPDZmi,\n    VPERMILPDZmik,\n    VPERMILPDZmikz,\n    VPERMILPDZrm,\n    VPERMILPDZrmb,\n    VPERMILPDZrmbk,\n    VPERMILPDZrmbkz,\n    VPERMILPDZrmk,\n    VPERMILPDZrmkz,\n    VPERMILPSZ128mbi,\n    VPERMILPSZ128mbik,\n    VPERMILPSZ128mbikz,\n    VPERMILPSZ128mi,\n    VPERMILPSZ128mik,\n    VPERMILPSZ128mikz,\n    VPERMILPSZ128rm,\n    VPERMILPSZ128rmb,\n    VPERMILPSZ128rmbk,\n    VPERMILPSZ128rmbkz,\n    VPERMILPSZ128rmk,\n    VPERMILPSZ128rmkz,\n    VPERMILPSZ256mbi,\n    VPERMILPSZ256mbik,\n    VPERMILPSZ256mbikz,\n    VPERMILPSZ256mi,\n    VPERMILPSZ256mik,\n    VPERMILPSZ256mikz,\n    VPERMILPSZ256rm,\n    VPERMILPSZ256rmb,\n    VPERMILPSZ256rmbk,\n    VPERMILPSZ256rmbkz,\n    VPERMILPSZ256rmk,\n    VPERMILPSZ256rmkz,\n    VPERMILPSZmbi,\n    VPERMILPSZmbik,\n    VPERMILPSZmbikz,\n    VPERMILPSZmi,\n    VPERMILPSZmik,\n    VPERMILPSZmikz,\n    VPERMILPSZrm,\n    VPERMILPSZrmb,\n    VPERMILPSZrmbk,\n    VPERMILPSZrmbkz,\n    VPERMILPSZrmk,\n    VPERMILPSZrmkz,\n    VPERMPDZ256mbi,\n    VPERMPDZ256mbik,\n    VPERMPDZ256mbikz,\n    VPERMPDZ256mi,\n    VPERMPDZ256mik,\n    VPERMPDZ256mikz,\n    VPERMPDZ256rm,\n    VPERMPDZ256rmb,\n    VPERMPDZ256rmbk,\n    VPERMPDZ256rmbkz,\n    VPERMPDZ256rmk,\n    VPERMPDZ256rmkz,\n    VPERMPDZmbi,\n    VPERMPDZmbik,\n    VPERMPDZmbikz,\n    VPERMPDZmi,\n    VPERMPDZmik,\n    VPERMPDZmikz,\n    VPERMPDZrm,\n    VPERMPDZrmb,\n    VPERMPDZrmbk,\n    VPERMPDZrmbkz,\n    VPERMPDZrmk,\n    VPERMPDZrmkz,\n    VPERMPSZ256rm,\n    VPERMPSZ256rmb,\n    VPERMPSZ256rmbk,\n    VPERMPSZ256rmbkz,\n    VPERMPSZ256rmk,\n    VPERMPSZ256rmkz,\n    VPERMPSZrm,\n    VPERMPSZrmb,\n    VPERMPSZrmbk,\n    VPERMPSZrmbkz,\n    VPERMPSZrmk,\n    VPERMPSZrmkz,\n    VPERMQZ256mbi,\n    VPERMQZ256mbik,\n    VPERMQZ256mbikz,\n    VPERMQZ256mi,\n    VPERMQZ256mik,\n    VPERMQZ256mikz,\n    VPERMQZ256rm,\n    VPERMQZ256rmb,\n    VPERMQZ256rmbk,\n    VPERMQZ256rmbkz,\n    VPERMQZ256rmk,\n    VPERMQZ256rmkz,\n    VPERMQZmbi,\n    VPERMQZmbik,\n    VPERMQZmbikz,\n    VPERMQZmi,\n    VPERMQZmik,\n    VPERMQZmikz,\n    VPERMQZrm,\n    VPERMQZrmb,\n    VPERMQZrmbk,\n    VPERMQZrmbkz,\n    VPERMQZrmk,\n    VPERMQZrmkz,\n    VPERMT2BZ128rm,\n    VPERMT2BZ128rmk,\n    VPERMT2BZ128rmkz,\n    VPERMT2BZ256rm,\n    VPERMT2BZ256rmk,\n    VPERMT2BZ256rmkz,\n    VPERMT2BZrm,\n    VPERMT2BZrmk,\n    VPERMT2BZrmkz,\n    VPERMT2DZ128rm,\n    VPERMT2DZ128rmb,\n    VPERMT2DZ128rmbk,\n    VPERMT2DZ128rmbkz,\n    VPERMT2DZ128rmk,\n    VPERMT2DZ128rmkz,\n    VPERMT2DZ256rm,\n    VPERMT2DZ256rmb,\n    VPERMT2DZ256rmbk,\n    VPERMT2DZ256rmbkz,\n    VPERMT2DZ256rmk,\n    VPERMT2DZ256rmkz,\n    VPERMT2DZrm,\n    VPERMT2DZrmb,\n    VPERMT2DZrmbk,\n    VPERMT2DZrmbkz,\n    VPERMT2DZrmk,\n    VPERMT2DZrmkz,\n    VPERMT2PDZ128rm,\n    VPERMT2PDZ128rmb,\n    VPERMT2PDZ128rmbk,\n    VPERMT2PDZ128rmbkz,\n    VPERMT2PDZ128rmk,\n    VPERMT2PDZ128rmkz,\n    VPERMT2PDZ256rm,\n    VPERMT2PDZ256rmb,\n    VPERMT2PDZ256rmbk,\n    VPERMT2PDZ256rmbkz,\n    VPERMT2PDZ256rmk,\n    VPERMT2PDZ256rmkz,\n    VPERMT2PDZrm,\n    VPERMT2PDZrmb,\n    VPERMT2PDZrmbk,\n    VPERMT2PDZrmbkz,\n    VPERMT2PDZrmk,\n    VPERMT2PDZrmkz,\n    VPERMT2PSZ128rm,\n    VPERMT2PSZ128rmb,\n    VPERMT2PSZ128rmbk,\n    VPERMT2PSZ128rmbkz,\n    VPERMT2PSZ128rmk,\n    VPERMT2PSZ128rmkz,\n    VPERMT2PSZ256rm,\n    VPERMT2PSZ256rmb,\n    VPERMT2PSZ256rmbk,\n    VPERMT2PSZ256rmbkz,\n    VPERMT2PSZ256rmk,\n    VPERMT2PSZ256rmkz,\n    VPERMT2PSZrm,\n    VPERMT2PSZrmb,\n    VPERMT2PSZrmbk,\n    VPERMT2PSZrmbkz,\n    VPERMT2PSZrmk,\n    VPERMT2PSZrmkz,\n    VPERMT2QZ128rm,\n    VPERMT2QZ128rmb,\n    VPERMT2QZ128rmbk,\n    VPERMT2QZ128rmbkz,\n    VPERMT2QZ128rmk,\n    VPERMT2QZ128rmkz,\n    VPERMT2QZ256rm,\n    VPERMT2QZ256rmb,\n    VPERMT2QZ256rmbk,\n    VPERMT2QZ256rmbkz,\n    VPERMT2QZ256rmk,\n    VPERMT2QZ256rmkz,\n    VPERMT2QZrm,\n    VPERMT2QZrmb,\n    VPERMT2QZrmbk,\n    VPERMT2QZrmbkz,\n    VPERMT2QZrmk,\n    VPERMT2QZrmkz,\n    VPERMT2WZ128rm,\n    VPERMT2WZ128rmk,\n    VPERMT2WZ128rmkz,\n    VPERMT2WZ256rm,\n    VPERMT2WZ256rmk,\n    VPERMT2WZ256rmkz,\n    VPERMT2WZrm,\n    VPERMT2WZrmk,\n    VPERMT2WZrmkz,\n    VPERMWZ128rm,\n    VPERMWZ128rmk,\n    VPERMWZ128rmkz,\n    VPERMWZ256rm,\n    VPERMWZ256rmk,\n    VPERMWZ256rmkz,\n    VPERMWZrm,\n    VPERMWZrmk,\n    VPERMWZrmkz,\n    VPEXPANDBZ128rmk,\n    VPEXPANDBZ128rmkz,\n    VPEXPANDBZ256rmk,\n    VPEXPANDBZ256rmkz,\n    VPEXPANDBZrmk,\n    VPEXPANDBZrmkz,\n    VPEXPANDDZ128rmk,\n    VPEXPANDDZ128rmkz,\n    VPEXPANDDZ256rmk,\n    VPEXPANDDZ256rmkz,\n    VPEXPANDDZrmk,\n    VPEXPANDDZrmkz,\n    VPEXPANDQZ128rmk,\n    VPEXPANDQZ128rmkz,\n    VPEXPANDQZ256rmk,\n    VPEXPANDQZ256rmkz,\n    VPEXPANDQZrmk,\n    VPEXPANDQZrmkz,\n    VPEXPANDWZ128rmk,\n    VPEXPANDWZ128rmkz,\n    VPEXPANDWZ256rmk,\n    VPEXPANDWZ256rmkz,\n    VPEXPANDWZrmk,\n    VPEXPANDWZrmkz,\n    VPEXTRBZmr,\n    VPEXTRDZmr,\n    VPEXTRQZmr,\n    VPEXTRWZmr,\n    VPGATHERDDZ128rm,\n    VPGATHERDDZ256rm,\n    VPGATHERDDZrm,\n    VPGATHERDQZ128rm,\n    VPGATHERDQZ256rm,\n    VPGATHERDQZrm,\n    VPGATHERQDZ128rm,\n    VPGATHERQDZ256rm,\n    VPGATHERQDZrm,\n    VPGATHERQQZ128rm,\n    VPGATHERQQZ256rm,\n    VPGATHERQQZrm,\n    VPINSRBZrm,\n    VPINSRDZrm,\n    VPINSRQZrm,\n    VPINSRWZrm,\n    VPLZCNTDZ128rm,\n    VPLZCNTDZ128rmb,\n    VPLZCNTDZ128rmbk,\n    VPLZCNTDZ128rmbkz,\n    VPLZCNTDZ128rmk,\n    VPLZCNTDZ128rmkz,\n    VPLZCNTDZ256rm,\n    VPLZCNTDZ256rmb,\n    VPLZCNTDZ256rmbk,\n    VPLZCNTDZ256rmbkz,\n    VPLZCNTDZ256rmk,\n    VPLZCNTDZ256rmkz,\n    VPLZCNTDZrm,\n    VPLZCNTDZrmb,\n    VPLZCNTDZrmbk,\n    VPLZCNTDZrmbkz,\n    VPLZCNTDZrmk,\n    VPLZCNTDZrmkz,\n    VPLZCNTQZ128rm,\n    VPLZCNTQZ128rmb,\n    VPLZCNTQZ128rmbk,\n    VPLZCNTQZ128rmbkz,\n    VPLZCNTQZ128rmk,\n    VPLZCNTQZ128rmkz,\n    VPLZCNTQZ256rm,\n    VPLZCNTQZ256rmb,\n    VPLZCNTQZ256rmbk,\n    VPLZCNTQZ256rmbkz,\n    VPLZCNTQZ256rmk,\n    VPLZCNTQZ256rmkz,\n    VPLZCNTQZrm,\n    VPLZCNTQZrmb,\n    VPLZCNTQZrmbk,\n    VPLZCNTQZrmbkz,\n    VPLZCNTQZrmk,\n    VPLZCNTQZrmkz,\n    VPMADD52HUQZ128m,\n    VPMADD52HUQZ128mb,\n    VPMADD52HUQZ128mbk,\n    VPMADD52HUQZ128mbkz,\n    VPMADD52HUQZ128mk,\n    VPMADD52HUQZ128mkz,\n    VPMADD52HUQZ256m,\n    VPMADD52HUQZ256mb,\n    VPMADD52HUQZ256mbk,\n    VPMADD52HUQZ256mbkz,\n    VPMADD52HUQZ256mk,\n    VPMADD52HUQZ256mkz,\n    VPMADD52HUQZm,\n    VPMADD52HUQZmb,\n    VPMADD52HUQZmbk,\n    VPMADD52HUQZmbkz,\n    VPMADD52HUQZmk,\n    VPMADD52HUQZmkz,\n    VPMADD52LUQZ128m,\n    VPMADD52LUQZ128mb,\n    VPMADD52LUQZ128mbk,\n    VPMADD52LUQZ128mbkz,\n    VPMADD52LUQZ128mk,\n    VPMADD52LUQZ128mkz,\n    VPMADD52LUQZ256m,\n    VPMADD52LUQZ256mb,\n    VPMADD52LUQZ256mbk,\n    VPMADD52LUQZ256mbkz,\n    VPMADD52LUQZ256mk,\n    VPMADD52LUQZ256mkz,\n    VPMADD52LUQZm,\n    VPMADD52LUQZmb,\n    VPMADD52LUQZmbk,\n    VPMADD52LUQZmbkz,\n    VPMADD52LUQZmk,\n    VPMADD52LUQZmkz,\n    VPMADDUBSWZ128rm,\n    VPMADDUBSWZ128rmk,\n    VPMADDUBSWZ128rmkz,\n    VPMADDUBSWZ256rm,\n    VPMADDUBSWZ256rmk,\n    VPMADDUBSWZ256rmkz,\n    VPMADDUBSWZrm,\n    VPMADDUBSWZrmk,\n    VPMADDUBSWZrmkz,\n    VPMADDWDZ128rm,\n    VPMADDWDZ128rmk,\n    VPMADDWDZ128rmkz,\n    VPMADDWDZ256rm,\n    VPMADDWDZ256rmk,\n    VPMADDWDZ256rmkz,\n    VPMADDWDZrm,\n    VPMADDWDZrmk,\n    VPMADDWDZrmkz,\n    VPMAXSBZ128rm,\n    VPMAXSBZ128rmk,\n    VPMAXSBZ128rmkz,\n    VPMAXSBZ256rm,\n    VPMAXSBZ256rmk,\n    VPMAXSBZ256rmkz,\n    VPMAXSBZrm,\n    VPMAXSBZrmk,\n    VPMAXSBZrmkz,\n    VPMAXSDZ128rm,\n    VPMAXSDZ128rmb,\n    VPMAXSDZ128rmbk,\n    VPMAXSDZ128rmbkz,\n    VPMAXSDZ128rmk,\n    VPMAXSDZ128rmkz,\n    VPMAXSDZ256rm,\n    VPMAXSDZ256rmb,\n    VPMAXSDZ256rmbk,\n    VPMAXSDZ256rmbkz,\n    VPMAXSDZ256rmk,\n    VPMAXSDZ256rmkz,\n    VPMAXSDZrm,\n    VPMAXSDZrmb,\n    VPMAXSDZrmbk,\n    VPMAXSDZrmbkz,\n    VPMAXSDZrmk,\n    VPMAXSDZrmkz,\n    VPMAXSQZ128rm,\n    VPMAXSQZ128rmb,\n    VPMAXSQZ128rmbk,\n    VPMAXSQZ128rmbkz,\n    VPMAXSQZ128rmk,\n    VPMAXSQZ128rmkz,\n    VPMAXSQZ256rm,\n    VPMAXSQZ256rmb,\n    VPMAXSQZ256rmbk,\n    VPMAXSQZ256rmbkz,\n    VPMAXSQZ256rmk,\n    VPMAXSQZ256rmkz,\n    VPMAXSQZrm,\n    VPMAXSQZrmb,\n    VPMAXSQZrmbk,\n    VPMAXSQZrmbkz,\n    VPMAXSQZrmk,\n    VPMAXSQZrmkz,\n    VPMAXSWZ128rm,\n    VPMAXSWZ128rmk,\n    VPMAXSWZ128rmkz,\n    VPMAXSWZ256rm,\n    VPMAXSWZ256rmk,\n    VPMAXSWZ256rmkz,\n    VPMAXSWZrm,\n    VPMAXSWZrmk,\n    VPMAXSWZrmkz,\n    VPMAXUBZ128rm,\n    VPMAXUBZ128rmk,\n    VPMAXUBZ128rmkz,\n    VPMAXUBZ256rm,\n    VPMAXUBZ256rmk,\n    VPMAXUBZ256rmkz,\n    VPMAXUBZrm,\n    VPMAXUBZrmk,\n    VPMAXUBZrmkz,\n    VPMAXUDZ128rm,\n    VPMAXUDZ128rmb,\n    VPMAXUDZ128rmbk,\n    VPMAXUDZ128rmbkz,\n    VPMAXUDZ128rmk,\n    VPMAXUDZ128rmkz,\n    VPMAXUDZ256rm,\n    VPMAXUDZ256rmb,\n    VPMAXUDZ256rmbk,\n    VPMAXUDZ256rmbkz,\n    VPMAXUDZ256rmk,\n    VPMAXUDZ256rmkz,\n    VPMAXUDZrm,\n    VPMAXUDZrmb,\n    VPMAXUDZrmbk,\n    VPMAXUDZrmbkz,\n    VPMAXUDZrmk,\n    VPMAXUDZrmkz,\n    VPMAXUQZ128rm,\n    VPMAXUQZ128rmb,\n    VPMAXUQZ128rmbk,\n    VPMAXUQZ128rmbkz,\n    VPMAXUQZ128rmk,\n    VPMAXUQZ128rmkz,\n    VPMAXUQZ256rm,\n    VPMAXUQZ256rmb,\n    VPMAXUQZ256rmbk,\n    VPMAXUQZ256rmbkz,\n    VPMAXUQZ256rmk,\n    VPMAXUQZ256rmkz,\n    VPMAXUQZrm,\n    VPMAXUQZrmb,\n    VPMAXUQZrmbk,\n    VPMAXUQZrmbkz,\n    VPMAXUQZrmk,\n    VPMAXUQZrmkz,\n    VPMAXUWZ128rm,\n    VPMAXUWZ128rmk,\n    VPMAXUWZ128rmkz,\n    VPMAXUWZ256rm,\n    VPMAXUWZ256rmk,\n    VPMAXUWZ256rmkz,\n    VPMAXUWZrm,\n    VPMAXUWZrmk,\n    VPMAXUWZrmkz,\n    VPMINSBZ128rm,\n    VPMINSBZ128rmk,\n    VPMINSBZ128rmkz,\n    VPMINSBZ256rm,\n    VPMINSBZ256rmk,\n    VPMINSBZ256rmkz,\n    VPMINSBZrm,\n    VPMINSBZrmk,\n    VPMINSBZrmkz,\n    VPMINSDZ128rm,\n    VPMINSDZ128rmb,\n    VPMINSDZ128rmbk,\n    VPMINSDZ128rmbkz,\n    VPMINSDZ128rmk,\n    VPMINSDZ128rmkz,\n    VPMINSDZ256rm,\n    VPMINSDZ256rmb,\n    VPMINSDZ256rmbk,\n    VPMINSDZ256rmbkz,\n    VPMINSDZ256rmk,\n    VPMINSDZ256rmkz,\n    VPMINSDZrm,\n    VPMINSDZrmb,\n    VPMINSDZrmbk,\n    VPMINSDZrmbkz,\n    VPMINSDZrmk,\n    VPMINSDZrmkz,\n    VPMINSQZ128rm,\n    VPMINSQZ128rmb,\n    VPMINSQZ128rmbk,\n    VPMINSQZ128rmbkz,\n    VPMINSQZ128rmk,\n    VPMINSQZ128rmkz,\n    VPMINSQZ256rm,\n    VPMINSQZ256rmb,\n    VPMINSQZ256rmbk,\n    VPMINSQZ256rmbkz,\n    VPMINSQZ256rmk,\n    VPMINSQZ256rmkz,\n    VPMINSQZrm,\n    VPMINSQZrmb,\n    VPMINSQZrmbk,\n    VPMINSQZrmbkz,\n    VPMINSQZrmk,\n    VPMINSQZrmkz,\n    VPMINSWZ128rm,\n    VPMINSWZ128rmk,\n    VPMINSWZ128rmkz,\n    VPMINSWZ256rm,\n    VPMINSWZ256rmk,\n    VPMINSWZ256rmkz,\n    VPMINSWZrm,\n    VPMINSWZrmk,\n    VPMINSWZrmkz,\n    VPMINUBZ128rm,\n    VPMINUBZ128rmk,\n    VPMINUBZ128rmkz,\n    VPMINUBZ256rm,\n    VPMINUBZ256rmk,\n    VPMINUBZ256rmkz,\n    VPMINUBZrm,\n    VPMINUBZrmk,\n    VPMINUBZrmkz,\n    VPMINUDZ128rm,\n    VPMINUDZ128rmb,\n    VPMINUDZ128rmbk,\n    VPMINUDZ128rmbkz,\n    VPMINUDZ128rmk,\n    VPMINUDZ128rmkz,\n    VPMINUDZ256rm,\n    VPMINUDZ256rmb,\n    VPMINUDZ256rmbk,\n    VPMINUDZ256rmbkz,\n    VPMINUDZ256rmk,\n    VPMINUDZ256rmkz,\n    VPMINUDZrm,\n    VPMINUDZrmb,\n    VPMINUDZrmbk,\n    VPMINUDZrmbkz,\n    VPMINUDZrmk,\n    VPMINUDZrmkz,\n    VPMINUQZ128rm,\n    VPMINUQZ128rmb,\n    VPMINUQZ128rmbk,\n    VPMINUQZ128rmbkz,\n    VPMINUQZ128rmk,\n    VPMINUQZ128rmkz,\n    VPMINUQZ256rm,\n    VPMINUQZ256rmb,\n    VPMINUQZ256rmbk,\n    VPMINUQZ256rmbkz,\n    VPMINUQZ256rmk,\n    VPMINUQZ256rmkz,\n    VPMINUQZrm,\n    VPMINUQZrmb,\n    VPMINUQZrmbk,\n    VPMINUQZrmbkz,\n    VPMINUQZrmk,\n    VPMINUQZrmkz,\n    VPMINUWZ128rm,\n    VPMINUWZ128rmk,\n    VPMINUWZ128rmkz,\n    VPMINUWZ256rm,\n    VPMINUWZ256rmk,\n    VPMINUWZ256rmkz,\n    VPMINUWZrm,\n    VPMINUWZrmk,\n    VPMINUWZrmkz,\n    VPMOVDBZ128mr,\n    VPMOVDBZ128mrk,\n    VPMOVDBZ256mr,\n    VPMOVDBZ256mrk,\n    VPMOVDBZmr,\n    VPMOVDBZmrk,\n    VPMOVDWZ128mr,\n    VPMOVDWZ128mrk,\n    VPMOVDWZ256mr,\n    VPMOVDWZ256mrk,\n    VPMOVDWZmr,\n    VPMOVDWZmrk,\n    VPMOVQBZ128mr,\n    VPMOVQBZ128mrk,\n    VPMOVQBZ256mr,\n    VPMOVQBZ256mrk,\n    VPMOVQBZmr,\n    VPMOVQBZmrk,\n    VPMOVQDZ128mr,\n    VPMOVQDZ128mrk,\n    VPMOVQDZ256mr,\n    VPMOVQDZ256mrk,\n    VPMOVQDZmr,\n    VPMOVQDZmrk,\n    VPMOVQWZ128mr,\n    VPMOVQWZ128mrk,\n    VPMOVQWZ256mr,\n    VPMOVQWZ256mrk,\n    VPMOVQWZmr,\n    VPMOVQWZmrk,\n    VPMOVSDBZ128mr,\n    VPMOVSDBZ128mrk,\n    VPMOVSDBZ256mr,\n    VPMOVSDBZ256mrk,\n    VPMOVSDBZmr,\n    VPMOVSDBZmrk,\n    VPMOVSDWZ128mr,\n    VPMOVSDWZ128mrk,\n    VPMOVSDWZ256mr,\n    VPMOVSDWZ256mrk,\n    VPMOVSDWZmr,\n    VPMOVSDWZmrk,\n    VPMOVSQBZ128mr,\n    VPMOVSQBZ128mrk,\n    VPMOVSQBZ256mr,\n    VPMOVSQBZ256mrk,\n    VPMOVSQBZmr,\n    VPMOVSQBZmrk,\n    VPMOVSQDZ128mr,\n    VPMOVSQDZ128mrk,\n    VPMOVSQDZ256mr,\n    VPMOVSQDZ256mrk,\n    VPMOVSQDZmr,\n    VPMOVSQDZmrk,\n    VPMOVSQWZ128mr,\n    VPMOVSQWZ128mrk,\n    VPMOVSQWZ256mr,\n    VPMOVSQWZ256mrk,\n    VPMOVSQWZmr,\n    VPMOVSQWZmrk,\n    VPMOVSWBZ128mr,\n    VPMOVSWBZ128mrk,\n    VPMOVSWBZ256mr,\n    VPMOVSWBZ256mrk,\n    VPMOVSWBZmr,\n    VPMOVSWBZmrk,\n    VPMOVSXBDZ128rm,\n    VPMOVSXBDZ128rmk,\n    VPMOVSXBDZ128rmkz,\n    VPMOVSXBDZ256rm,\n    VPMOVSXBDZ256rmk,\n    VPMOVSXBDZ256rmkz,\n    VPMOVSXBDZrm,\n    VPMOVSXBDZrmk,\n    VPMOVSXBDZrmkz,\n    VPMOVSXBQZ128rm,\n    VPMOVSXBQZ128rmk,\n    VPMOVSXBQZ128rmkz,\n    VPMOVSXBQZ256rm,\n    VPMOVSXBQZ256rmk,\n    VPMOVSXBQZ256rmkz,\n    VPMOVSXBQZrm,\n    VPMOVSXBQZrmk,\n    VPMOVSXBQZrmkz,\n    VPMOVSXBWZ128rm,\n    VPMOVSXBWZ128rmk,\n    VPMOVSXBWZ128rmkz,\n    VPMOVSXBWZ256rm,\n    VPMOVSXBWZ256rmk,\n    VPMOVSXBWZ256rmkz,\n    VPMOVSXBWZrm,\n    VPMOVSXBWZrmk,\n    VPMOVSXBWZrmkz,\n    VPMOVSXDQZ128rm,\n    VPMOVSXDQZ128rmk,\n    VPMOVSXDQZ128rmkz,\n    VPMOVSXDQZ256rm,\n    VPMOVSXDQZ256rmk,\n    VPMOVSXDQZ256rmkz,\n    VPMOVSXDQZrm,\n    VPMOVSXDQZrmk,\n    VPMOVSXDQZrmkz,\n    VPMOVSXWDZ128rm,\n    VPMOVSXWDZ128rmk,\n    VPMOVSXWDZ128rmkz,\n    VPMOVSXWDZ256rm,\n    VPMOVSXWDZ256rmk,\n    VPMOVSXWDZ256rmkz,\n    VPMOVSXWDZrm,\n    VPMOVSXWDZrmk,\n    VPMOVSXWDZrmkz,\n    VPMOVSXWQZ128rm,\n    VPMOVSXWQZ128rmk,\n    VPMOVSXWQZ128rmkz,\n    VPMOVSXWQZ256rm,\n    VPMOVSXWQZ256rmk,\n    VPMOVSXWQZ256rmkz,\n    VPMOVSXWQZrm,\n    VPMOVSXWQZrmk,\n    VPMOVSXWQZrmkz,\n    VPMOVUSDBZ128mr,\n    VPMOVUSDBZ128mrk,\n    VPMOVUSDBZ256mr,\n    VPMOVUSDBZ256mrk,\n    VPMOVUSDBZmr,\n    VPMOVUSDBZmrk,\n    VPMOVUSDWZ128mr,\n    VPMOVUSDWZ128mrk,\n    VPMOVUSDWZ256mr,\n    VPMOVUSDWZ256mrk,\n    VPMOVUSDWZmr,\n    VPMOVUSDWZmrk,\n    VPMOVUSQBZ128mr,\n    VPMOVUSQBZ128mrk,\n    VPMOVUSQBZ256mr,\n    VPMOVUSQBZ256mrk,\n    VPMOVUSQBZmr,\n    VPMOVUSQBZmrk,\n    VPMOVUSQDZ128mr,\n    VPMOVUSQDZ128mrk,\n    VPMOVUSQDZ256mr,\n    VPMOVUSQDZ256mrk,\n    VPMOVUSQDZmr,\n    VPMOVUSQDZmrk,\n    VPMOVUSQWZ128mr,\n    VPMOVUSQWZ128mrk,\n    VPMOVUSQWZ256mr,\n    VPMOVUSQWZ256mrk,\n    VPMOVUSQWZmr,\n    VPMOVUSQWZmrk,\n    VPMOVUSWBZ128mr,\n    VPMOVUSWBZ128mrk,\n    VPMOVUSWBZ256mr,\n    VPMOVUSWBZ256mrk,\n    VPMOVUSWBZmr,\n    VPMOVUSWBZmrk,\n    VPMOVWBZ128mr,\n    VPMOVWBZ128mrk,\n    VPMOVWBZ256mr,\n    VPMOVWBZ256mrk,\n    VPMOVWBZmr,\n    VPMOVWBZmrk,\n    VPMOVZXBDZ128rm,\n    VPMOVZXBDZ128rmk,\n    VPMOVZXBDZ128rmkz,\n    VPMOVZXBDZ256rm,\n    VPMOVZXBDZ256rmk,\n    VPMOVZXBDZ256rmkz,\n    VPMOVZXBDZrm,\n    VPMOVZXBDZrmk,\n    VPMOVZXBDZrmkz,\n    VPMOVZXBQZ128rm,\n    VPMOVZXBQZ128rmk,\n    VPMOVZXBQZ128rmkz,\n    VPMOVZXBQZ256rm,\n    VPMOVZXBQZ256rmk,\n    VPMOVZXBQZ256rmkz,\n    VPMOVZXBQZrm,\n    VPMOVZXBQZrmk,\n    VPMOVZXBQZrmkz,\n    VPMOVZXBWZ128rm,\n    VPMOVZXBWZ128rmk,\n    VPMOVZXBWZ128rmkz,\n    VPMOVZXBWZ256rm,\n    VPMOVZXBWZ256rmk,\n    VPMOVZXBWZ256rmkz,\n    VPMOVZXBWZrm,\n    VPMOVZXBWZrmk,\n    VPMOVZXBWZrmkz,\n    VPMOVZXDQZ128rm,\n    VPMOVZXDQZ128rmk,\n    VPMOVZXDQZ128rmkz,\n    VPMOVZXDQZ256rm,\n    VPMOVZXDQZ256rmk,\n    VPMOVZXDQZ256rmkz,\n    VPMOVZXDQZrm,\n    VPMOVZXDQZrmk,\n    VPMOVZXDQZrmkz,\n    VPMOVZXWDZ128rm,\n    VPMOVZXWDZ128rmk,\n    VPMOVZXWDZ128rmkz,\n    VPMOVZXWDZ256rm,\n    VPMOVZXWDZ256rmk,\n    VPMOVZXWDZ256rmkz,\n    VPMOVZXWDZrm,\n    VPMOVZXWDZrmk,\n    VPMOVZXWDZrmkz,\n    VPMOVZXWQZ128rm,\n    VPMOVZXWQZ128rmk,\n    VPMOVZXWQZ128rmkz,\n    VPMOVZXWQZ256rm,\n    VPMOVZXWQZ256rmk,\n    VPMOVZXWQZ256rmkz,\n    VPMOVZXWQZrm,\n    VPMOVZXWQZrmk,\n    VPMOVZXWQZrmkz,\n    VPMULDQZ128rm,\n    VPMULDQZ128rmb,\n    VPMULDQZ128rmbk,\n    VPMULDQZ128rmbkz,\n    VPMULDQZ128rmk,\n    VPMULDQZ128rmkz,\n    VPMULDQZ256rm,\n    VPMULDQZ256rmb,\n    VPMULDQZ256rmbk,\n    VPMULDQZ256rmbkz,\n    VPMULDQZ256rmk,\n    VPMULDQZ256rmkz,\n    VPMULDQZrm,\n    VPMULDQZrmb,\n    VPMULDQZrmbk,\n    VPMULDQZrmbkz,\n    VPMULDQZrmk,\n    VPMULDQZrmkz,\n    VPMULHRSWZ128rm,\n    VPMULHRSWZ128rmk,\n    VPMULHRSWZ128rmkz,\n    VPMULHRSWZ256rm,\n    VPMULHRSWZ256rmk,\n    VPMULHRSWZ256rmkz,\n    VPMULHRSWZrm,\n    VPMULHRSWZrmk,\n    VPMULHRSWZrmkz,\n    VPMULHUWZ128rm,\n    VPMULHUWZ128rmk,\n    VPMULHUWZ128rmkz,\n    VPMULHUWZ256rm,\n    VPMULHUWZ256rmk,\n    VPMULHUWZ256rmkz,\n    VPMULHUWZrm,\n    VPMULHUWZrmk,\n    VPMULHUWZrmkz,\n    VPMULHWZ128rm,\n    VPMULHWZ128rmk,\n    VPMULHWZ128rmkz,\n    VPMULHWZ256rm,\n    VPMULHWZ256rmk,\n    VPMULHWZ256rmkz,\n    VPMULHWZrm,\n    VPMULHWZrmk,\n    VPMULHWZrmkz,\n    VPMULLDZ128rm,\n    VPMULLDZ128rmb,\n    VPMULLDZ128rmbk,\n    VPMULLDZ128rmbkz,\n    VPMULLDZ128rmk,\n    VPMULLDZ128rmkz,\n    VPMULLDZ256rm,\n    VPMULLDZ256rmb,\n    VPMULLDZ256rmbk,\n    VPMULLDZ256rmbkz,\n    VPMULLDZ256rmk,\n    VPMULLDZ256rmkz,\n    VPMULLDZrm,\n    VPMULLDZrmb,\n    VPMULLDZrmbk,\n    VPMULLDZrmbkz,\n    VPMULLDZrmk,\n    VPMULLDZrmkz,\n    VPMULLQZ128rm,\n    VPMULLQZ128rmb,\n    VPMULLQZ128rmbk,\n    VPMULLQZ128rmbkz,\n    VPMULLQZ128rmk,\n    VPMULLQZ128rmkz,\n    VPMULLQZ256rm,\n    VPMULLQZ256rmb,\n    VPMULLQZ256rmbk,\n    VPMULLQZ256rmbkz,\n    VPMULLQZ256rmk,\n    VPMULLQZ256rmkz,\n    VPMULLQZrm,\n    VPMULLQZrmb,\n    VPMULLQZrmbk,\n    VPMULLQZrmbkz,\n    VPMULLQZrmk,\n    VPMULLQZrmkz,\n    VPMULLWZ128rm,\n    VPMULLWZ128rmk,\n    VPMULLWZ128rmkz,\n    VPMULLWZ256rm,\n    VPMULLWZ256rmk,\n    VPMULLWZ256rmkz,\n    VPMULLWZrm,\n    VPMULLWZrmk,\n    VPMULLWZrmkz,\n    VPMULTISHIFTQBZ128rm,\n    VPMULTISHIFTQBZ128rmb,\n    VPMULTISHIFTQBZ128rmbk,\n    VPMULTISHIFTQBZ128rmbkz,\n    VPMULTISHIFTQBZ128rmk,\n    VPMULTISHIFTQBZ128rmkz,\n    VPMULTISHIFTQBZ256rm,\n    VPMULTISHIFTQBZ256rmb,\n    VPMULTISHIFTQBZ256rmbk,\n    VPMULTISHIFTQBZ256rmbkz,\n    VPMULTISHIFTQBZ256rmk,\n    VPMULTISHIFTQBZ256rmkz,\n    VPMULTISHIFTQBZrm,\n    VPMULTISHIFTQBZrmb,\n    VPMULTISHIFTQBZrmbk,\n    VPMULTISHIFTQBZrmbkz,\n    VPMULTISHIFTQBZrmk,\n    VPMULTISHIFTQBZrmkz,\n    VPMULUDQZ128rm,\n    VPMULUDQZ128rmb,\n    VPMULUDQZ128rmbk,\n    VPMULUDQZ128rmbkz,\n    VPMULUDQZ128rmk,\n    VPMULUDQZ128rmkz,\n    VPMULUDQZ256rm,\n    VPMULUDQZ256rmb,\n    VPMULUDQZ256rmbk,\n    VPMULUDQZ256rmbkz,\n    VPMULUDQZ256rmk,\n    VPMULUDQZ256rmkz,\n    VPMULUDQZrm,\n    VPMULUDQZrmb,\n    VPMULUDQZrmbk,\n    VPMULUDQZrmbkz,\n    VPMULUDQZrmk,\n    VPMULUDQZrmkz,\n    VPOPCNTBZ128rm,\n    VPOPCNTBZ128rmk,\n    VPOPCNTBZ128rmkz,\n    VPOPCNTBZ256rm,\n    VPOPCNTBZ256rmk,\n    VPOPCNTBZ256rmkz,\n    VPOPCNTBZrm,\n    VPOPCNTBZrmk,\n    VPOPCNTBZrmkz,\n    VPOPCNTDZ128rm,\n    VPOPCNTDZ128rmb,\n    VPOPCNTDZ128rmbk,\n    VPOPCNTDZ128rmbkz,\n    VPOPCNTDZ128rmk,\n    VPOPCNTDZ128rmkz,\n    VPOPCNTDZ256rm,\n    VPOPCNTDZ256rmb,\n    VPOPCNTDZ256rmbk,\n    VPOPCNTDZ256rmbkz,\n    VPOPCNTDZ256rmk,\n    VPOPCNTDZ256rmkz,\n    VPOPCNTDZrm,\n    VPOPCNTDZrmb,\n    VPOPCNTDZrmbk,\n    VPOPCNTDZrmbkz,\n    VPOPCNTDZrmk,\n    VPOPCNTDZrmkz,\n    VPOPCNTQZ128rm,\n    VPOPCNTQZ128rmb,\n    VPOPCNTQZ128rmbk,\n    VPOPCNTQZ128rmbkz,\n    VPOPCNTQZ128rmk,\n    VPOPCNTQZ128rmkz,\n    VPOPCNTQZ256rm,\n    VPOPCNTQZ256rmb,\n    VPOPCNTQZ256rmbk,\n    VPOPCNTQZ256rmbkz,\n    VPOPCNTQZ256rmk,\n    VPOPCNTQZ256rmkz,\n    VPOPCNTQZrm,\n    VPOPCNTQZrmb,\n    VPOPCNTQZrmbk,\n    VPOPCNTQZrmbkz,\n    VPOPCNTQZrmk,\n    VPOPCNTQZrmkz,\n    VPOPCNTWZ128rm,\n    VPOPCNTWZ128rmk,\n    VPOPCNTWZ128rmkz,\n    VPOPCNTWZ256rm,\n    VPOPCNTWZ256rmk,\n    VPOPCNTWZ256rmkz,\n    VPOPCNTWZrm,\n    VPOPCNTWZrmk,\n    VPOPCNTWZrmkz,\n    VPORDZ128rm,\n    VPORDZ128rmb,\n    VPORDZ128rmbk,\n    VPORDZ128rmbkz,\n    VPORDZ128rmk,\n    VPORDZ128rmkz,\n    VPORDZ256rm,\n    VPORDZ256rmb,\n    VPORDZ256rmbk,\n    VPORDZ256rmbkz,\n    VPORDZ256rmk,\n    VPORDZ256rmkz,\n    VPORDZrm,\n    VPORDZrmb,\n    VPORDZrmbk,\n    VPORDZrmbkz,\n    VPORDZrmk,\n    VPORDZrmkz,\n    VPORQZ128rm,\n    VPORQZ128rmb,\n    VPORQZ128rmbk,\n    VPORQZ128rmbkz,\n    VPORQZ128rmk,\n    VPORQZ128rmkz,\n    VPORQZ256rm,\n    VPORQZ256rmb,\n    VPORQZ256rmbk,\n    VPORQZ256rmbkz,\n    VPORQZ256rmk,\n    VPORQZ256rmkz,\n    VPORQZrm,\n    VPORQZrmb,\n    VPORQZrmbk,\n    VPORQZrmbkz,\n    VPORQZrmk,\n    VPORQZrmkz,\n    VPROLDZ128mbi,\n    VPROLDZ128mbik,\n    VPROLDZ128mbikz,\n    VPROLDZ128mi,\n    VPROLDZ128mik,\n    VPROLDZ128mikz,\n    VPROLDZ256mbi,\n    VPROLDZ256mbik,\n    VPROLDZ256mbikz,\n    VPROLDZ256mi,\n    VPROLDZ256mik,\n    VPROLDZ256mikz,\n    VPROLDZmbi,\n    VPROLDZmbik,\n    VPROLDZmbikz,\n    VPROLDZmi,\n    VPROLDZmik,\n    VPROLDZmikz,\n    VPROLQZ128mbi,\n    VPROLQZ128mbik,\n    VPROLQZ128mbikz,\n    VPROLQZ128mi,\n    VPROLQZ128mik,\n    VPROLQZ128mikz,\n    VPROLQZ256mbi,\n    VPROLQZ256mbik,\n    VPROLQZ256mbikz,\n    VPROLQZ256mi,\n    VPROLQZ256mik,\n    VPROLQZ256mikz,\n    VPROLQZmbi,\n    VPROLQZmbik,\n    VPROLQZmbikz,\n    VPROLQZmi,\n    VPROLQZmik,\n    VPROLQZmikz,\n    VPROLVDZ128rm,\n    VPROLVDZ128rmb,\n    VPROLVDZ128rmbk,\n    VPROLVDZ128rmbkz,\n    VPROLVDZ128rmk,\n    VPROLVDZ128rmkz,\n    VPROLVDZ256rm,\n    VPROLVDZ256rmb,\n    VPROLVDZ256rmbk,\n    VPROLVDZ256rmbkz,\n    VPROLVDZ256rmk,\n    VPROLVDZ256rmkz,\n    VPROLVDZrm,\n    VPROLVDZrmb,\n    VPROLVDZrmbk,\n    VPROLVDZrmbkz,\n    VPROLVDZrmk,\n    VPROLVDZrmkz,\n    VPROLVQZ128rm,\n    VPROLVQZ128rmb,\n    VPROLVQZ128rmbk,\n    VPROLVQZ128rmbkz,\n    VPROLVQZ128rmk,\n    VPROLVQZ128rmkz,\n    VPROLVQZ256rm,\n    VPROLVQZ256rmb,\n    VPROLVQZ256rmbk,\n    VPROLVQZ256rmbkz,\n    VPROLVQZ256rmk,\n    VPROLVQZ256rmkz,\n    VPROLVQZrm,\n    VPROLVQZrmb,\n    VPROLVQZrmbk,\n    VPROLVQZrmbkz,\n    VPROLVQZrmk,\n    VPROLVQZrmkz,\n    VPRORDZ128mbi,\n    VPRORDZ128mbik,\n    VPRORDZ128mbikz,\n    VPRORDZ128mi,\n    VPRORDZ128mik,\n    VPRORDZ128mikz,\n    VPRORDZ256mbi,\n    VPRORDZ256mbik,\n    VPRORDZ256mbikz,\n    VPRORDZ256mi,\n    VPRORDZ256mik,\n    VPRORDZ256mikz,\n    VPRORDZmbi,\n    VPRORDZmbik,\n    VPRORDZmbikz,\n    VPRORDZmi,\n    VPRORDZmik,\n    VPRORDZmikz,\n    VPRORQZ128mbi,\n    VPRORQZ128mbik,\n    VPRORQZ128mbikz,\n    VPRORQZ128mi,\n    VPRORQZ128mik,\n    VPRORQZ128mikz,\n    VPRORQZ256mbi,\n    VPRORQZ256mbik,\n    VPRORQZ256mbikz,\n    VPRORQZ256mi,\n    VPRORQZ256mik,\n    VPRORQZ256mikz,\n    VPRORQZmbi,\n    VPRORQZmbik,\n    VPRORQZmbikz,\n    VPRORQZmi,\n    VPRORQZmik,\n    VPRORQZmikz,\n    VPRORVDZ128rm,\n    VPRORVDZ128rmb,\n    VPRORVDZ128rmbk,\n    VPRORVDZ128rmbkz,\n    VPRORVDZ128rmk,\n    VPRORVDZ128rmkz,\n    VPRORVDZ256rm,\n    VPRORVDZ256rmb,\n    VPRORVDZ256rmbk,\n    VPRORVDZ256rmbkz,\n    VPRORVDZ256rmk,\n    VPRORVDZ256rmkz,\n    VPRORVDZrm,\n    VPRORVDZrmb,\n    VPRORVDZrmbk,\n    VPRORVDZrmbkz,\n    VPRORVDZrmk,\n    VPRORVDZrmkz,\n    VPRORVQZ128rm,\n    VPRORVQZ128rmb,\n    VPRORVQZ128rmbk,\n    VPRORVQZ128rmbkz,\n    VPRORVQZ128rmk,\n    VPRORVQZ128rmkz,\n    VPRORVQZ256rm,\n    VPRORVQZ256rmb,\n    VPRORVQZ256rmbk,\n    VPRORVQZ256rmbkz,\n    VPRORVQZ256rmk,\n    VPRORVQZ256rmkz,\n    VPRORVQZrm,\n    VPRORVQZrmb,\n    VPRORVQZrmbk,\n    VPRORVQZrmbkz,\n    VPRORVQZrmk,\n    VPRORVQZrmkz,\n    VPSADBWZ128rm,\n    VPSADBWZ256rm,\n    VPSADBWZrm,\n    VPSCATTERDDZ128mr,\n    VPSCATTERDDZ256mr,\n    VPSCATTERDDZmr,\n    VPSCATTERDQZ128mr,\n    VPSCATTERDQZ256mr,\n    VPSCATTERDQZmr,\n    VPSCATTERQDZ128mr,\n    VPSCATTERQDZ256mr,\n    VPSCATTERQDZmr,\n    VPSCATTERQQZ128mr,\n    VPSCATTERQQZ256mr,\n    VPSCATTERQQZmr,\n    VPSHLDDZ128rmbi,\n    VPSHLDDZ128rmbik,\n    VPSHLDDZ128rmbikz,\n    VPSHLDDZ128rmi,\n    VPSHLDDZ128rmik,\n    VPSHLDDZ128rmikz,\n    VPSHLDDZ256rmbi,\n    VPSHLDDZ256rmbik,\n    VPSHLDDZ256rmbikz,\n    VPSHLDDZ256rmi,\n    VPSHLDDZ256rmik,\n    VPSHLDDZ256rmikz,\n    VPSHLDDZrmbi,\n    VPSHLDDZrmbik,\n    VPSHLDDZrmbikz,\n    VPSHLDDZrmi,\n    VPSHLDDZrmik,\n    VPSHLDDZrmikz,\n    VPSHLDQZ128rmbi,\n    VPSHLDQZ128rmbik,\n    VPSHLDQZ128rmbikz,\n    VPSHLDQZ128rmi,\n    VPSHLDQZ128rmik,\n    VPSHLDQZ128rmikz,\n    VPSHLDQZ256rmbi,\n    VPSHLDQZ256rmbik,\n    VPSHLDQZ256rmbikz,\n    VPSHLDQZ256rmi,\n    VPSHLDQZ256rmik,\n    VPSHLDQZ256rmikz,\n    VPSHLDQZrmbi,\n    VPSHLDQZrmbik,\n    VPSHLDQZrmbikz,\n    VPSHLDQZrmi,\n    VPSHLDQZrmik,\n    VPSHLDQZrmikz,\n    VPSHLDVDZ128m,\n    VPSHLDVDZ128mb,\n    VPSHLDVDZ128mbk,\n    VPSHLDVDZ128mbkz,\n    VPSHLDVDZ128mk,\n    VPSHLDVDZ128mkz,\n    VPSHLDVDZ256m,\n    VPSHLDVDZ256mb,\n    VPSHLDVDZ256mbk,\n    VPSHLDVDZ256mbkz,\n    VPSHLDVDZ256mk,\n    VPSHLDVDZ256mkz,\n    VPSHLDVDZm,\n    VPSHLDVDZmb,\n    VPSHLDVDZmbk,\n    VPSHLDVDZmbkz,\n    VPSHLDVDZmk,\n    VPSHLDVDZmkz,\n    VPSHLDVQZ128m,\n    VPSHLDVQZ128mb,\n    VPSHLDVQZ128mbk,\n    VPSHLDVQZ128mbkz,\n    VPSHLDVQZ128mk,\n    VPSHLDVQZ128mkz,\n    VPSHLDVQZ256m,\n    VPSHLDVQZ256mb,\n    VPSHLDVQZ256mbk,\n    VPSHLDVQZ256mbkz,\n    VPSHLDVQZ256mk,\n    VPSHLDVQZ256mkz,\n    VPSHLDVQZm,\n    VPSHLDVQZmb,\n    VPSHLDVQZmbk,\n    VPSHLDVQZmbkz,\n    VPSHLDVQZmk,\n    VPSHLDVQZmkz,\n    VPSHLDVWZ128m,\n    VPSHLDVWZ128mk,\n    VPSHLDVWZ128mkz,\n    VPSHLDVWZ256m,\n    VPSHLDVWZ256mk,\n    VPSHLDVWZ256mkz,\n    VPSHLDVWZm,\n    VPSHLDVWZmk,\n    VPSHLDVWZmkz,\n    VPSHLDWZ128rmi,\n    VPSHLDWZ128rmik,\n    VPSHLDWZ128rmikz,\n    VPSHLDWZ256rmi,\n    VPSHLDWZ256rmik,\n    VPSHLDWZ256rmikz,\n    VPSHLDWZrmi,\n    VPSHLDWZrmik,\n    VPSHLDWZrmikz,\n    VPSHRDDZ128rmbi,\n    VPSHRDDZ128rmbik,\n    VPSHRDDZ128rmbikz,\n    VPSHRDDZ128rmi,\n    VPSHRDDZ128rmik,\n    VPSHRDDZ128rmikz,\n    VPSHRDDZ256rmbi,\n    VPSHRDDZ256rmbik,\n    VPSHRDDZ256rmbikz,\n    VPSHRDDZ256rmi,\n    VPSHRDDZ256rmik,\n    VPSHRDDZ256rmikz,\n    VPSHRDDZrmbi,\n    VPSHRDDZrmbik,\n    VPSHRDDZrmbikz,\n    VPSHRDDZrmi,\n    VPSHRDDZrmik,\n    VPSHRDDZrmikz,\n    VPSHRDQZ128rmbi,\n    VPSHRDQZ128rmbik,\n    VPSHRDQZ128rmbikz,\n    VPSHRDQZ128rmi,\n    VPSHRDQZ128rmik,\n    VPSHRDQZ128rmikz,\n    VPSHRDQZ256rmbi,\n    VPSHRDQZ256rmbik,\n    VPSHRDQZ256rmbikz,\n    VPSHRDQZ256rmi,\n    VPSHRDQZ256rmik,\n    VPSHRDQZ256rmikz,\n    VPSHRDQZrmbi,\n    VPSHRDQZrmbik,\n    VPSHRDQZrmbikz,\n    VPSHRDQZrmi,\n    VPSHRDQZrmik,\n    VPSHRDQZrmikz,\n    VPSHRDVDZ128m,\n    VPSHRDVDZ128mb,\n    VPSHRDVDZ128mbk,\n    VPSHRDVDZ128mbkz,\n    VPSHRDVDZ128mk,\n    VPSHRDVDZ128mkz,\n    VPSHRDVDZ256m,\n    VPSHRDVDZ256mb,\n    VPSHRDVDZ256mbk,\n    VPSHRDVDZ256mbkz,\n    VPSHRDVDZ256mk,\n    VPSHRDVDZ256mkz,\n    VPSHRDVDZm,\n    VPSHRDVDZmb,\n    VPSHRDVDZmbk,\n    VPSHRDVDZmbkz,\n    VPSHRDVDZmk,\n    VPSHRDVDZmkz,\n    VPSHRDVQZ128m,\n    VPSHRDVQZ128mb,\n    VPSHRDVQZ128mbk,\n    VPSHRDVQZ128mbkz,\n    VPSHRDVQZ128mk,\n    VPSHRDVQZ128mkz,\n    VPSHRDVQZ256m,\n    VPSHRDVQZ256mb,\n    VPSHRDVQZ256mbk,\n    VPSHRDVQZ256mbkz,\n    VPSHRDVQZ256mk,\n    VPSHRDVQZ256mkz,\n    VPSHRDVQZm,\n    VPSHRDVQZmb,\n    VPSHRDVQZmbk,\n    VPSHRDVQZmbkz,\n    VPSHRDVQZmk,\n    VPSHRDVQZmkz,\n    VPSHRDVWZ128m,\n    VPSHRDVWZ128mk,\n    VPSHRDVWZ128mkz,\n    VPSHRDVWZ256m,\n    VPSHRDVWZ256mk,\n    VPSHRDVWZ256mkz,\n    VPSHRDVWZm,\n    VPSHRDVWZmk,\n    VPSHRDVWZmkz,\n    VPSHRDWZ128rmi,\n    VPSHRDWZ128rmik,\n    VPSHRDWZ128rmikz,\n    VPSHRDWZ256rmi,\n    VPSHRDWZ256rmik,\n    VPSHRDWZ256rmikz,\n    VPSHRDWZrmi,\n    VPSHRDWZrmik,\n    VPSHRDWZrmikz,\n    VPSHUFBITQMBZ128rm,\n    VPSHUFBITQMBZ128rmk,\n    VPSHUFBITQMBZ256rm,\n    VPSHUFBITQMBZ256rmk,\n    VPSHUFBITQMBZrm,\n    VPSHUFBITQMBZrmk,\n    VPSHUFBZ128rm,\n    VPSHUFBZ128rmk,\n    VPSHUFBZ128rmkz,\n    VPSHUFBZ256rm,\n    VPSHUFBZ256rmk,\n    VPSHUFBZ256rmkz,\n    VPSHUFBZrm,\n    VPSHUFBZrmk,\n    VPSHUFBZrmkz,\n    VPSHUFDZ128mbi,\n    VPSHUFDZ128mbik,\n    VPSHUFDZ128mbikz,\n    VPSHUFDZ128mi,\n    VPSHUFDZ128mik,\n    VPSHUFDZ128mikz,\n    VPSHUFDZ256mbi,\n    VPSHUFDZ256mbik,\n    VPSHUFDZ256mbikz,\n    VPSHUFDZ256mi,\n    VPSHUFDZ256mik,\n    VPSHUFDZ256mikz,\n    VPSHUFDZmbi,\n    VPSHUFDZmbik,\n    VPSHUFDZmbikz,\n    VPSHUFDZmi,\n    VPSHUFDZmik,\n    VPSHUFDZmikz,\n    VPSHUFHWZ128mi,\n    VPSHUFHWZ128mik,\n    VPSHUFHWZ128mikz,\n    VPSHUFHWZ256mi,\n    VPSHUFHWZ256mik,\n    VPSHUFHWZ256mikz,\n    VPSHUFHWZmi,\n    VPSHUFHWZmik,\n    VPSHUFHWZmikz,\n    VPSHUFLWZ128mi,\n    VPSHUFLWZ128mik,\n    VPSHUFLWZ128mikz,\n    VPSHUFLWZ256mi,\n    VPSHUFLWZ256mik,\n    VPSHUFLWZ256mikz,\n    VPSHUFLWZmi,\n    VPSHUFLWZmik,\n    VPSHUFLWZmikz,\n    VPSLLDQZ128mi,\n    VPSLLDQZ256mi,\n    VPSLLDQZmi,\n    VPSLLDZ128mbi,\n    VPSLLDZ128mbik,\n    VPSLLDZ128mbikz,\n    VPSLLDZ128mi,\n    VPSLLDZ128mik,\n    VPSLLDZ128mikz,\n    VPSLLDZ128rm,\n    VPSLLDZ128rmk,\n    VPSLLDZ128rmkz,\n    VPSLLDZ256mbi,\n    VPSLLDZ256mbik,\n    VPSLLDZ256mbikz,\n    VPSLLDZ256mi,\n    VPSLLDZ256mik,\n    VPSLLDZ256mikz,\n    VPSLLDZ256rm,\n    VPSLLDZ256rmk,\n    VPSLLDZ256rmkz,\n    VPSLLDZmbi,\n    VPSLLDZmbik,\n    VPSLLDZmbikz,\n    VPSLLDZmi,\n    VPSLLDZmik,\n    VPSLLDZmikz,\n    VPSLLDZrm,\n    VPSLLDZrmk,\n    VPSLLDZrmkz,\n    VPSLLQZ128mbi,\n    VPSLLQZ128mbik,\n    VPSLLQZ128mbikz,\n    VPSLLQZ128mi,\n    VPSLLQZ128mik,\n    VPSLLQZ128mikz,\n    VPSLLQZ128rm,\n    VPSLLQZ128rmk,\n    VPSLLQZ128rmkz,\n    VPSLLQZ256mbi,\n    VPSLLQZ256mbik,\n    VPSLLQZ256mbikz,\n    VPSLLQZ256mi,\n    VPSLLQZ256mik,\n    VPSLLQZ256mikz,\n    VPSLLQZ256rm,\n    VPSLLQZ256rmk,\n    VPSLLQZ256rmkz,\n    VPSLLQZmbi,\n    VPSLLQZmbik,\n    VPSLLQZmbikz,\n    VPSLLQZmi,\n    VPSLLQZmik,\n    VPSLLQZmikz,\n    VPSLLQZrm,\n    VPSLLQZrmk,\n    VPSLLQZrmkz,\n    VPSLLVDZ128rm,\n    VPSLLVDZ128rmb,\n    VPSLLVDZ128rmbk,\n    VPSLLVDZ128rmbkz,\n    VPSLLVDZ128rmk,\n    VPSLLVDZ128rmkz,\n    VPSLLVDZ256rm,\n    VPSLLVDZ256rmb,\n    VPSLLVDZ256rmbk,\n    VPSLLVDZ256rmbkz,\n    VPSLLVDZ256rmk,\n    VPSLLVDZ256rmkz,\n    VPSLLVDZrm,\n    VPSLLVDZrmb,\n    VPSLLVDZrmbk,\n    VPSLLVDZrmbkz,\n    VPSLLVDZrmk,\n    VPSLLVDZrmkz,\n    VPSLLVQZ128rm,\n    VPSLLVQZ128rmb,\n    VPSLLVQZ128rmbk,\n    VPSLLVQZ128rmbkz,\n    VPSLLVQZ128rmk,\n    VPSLLVQZ128rmkz,\n    VPSLLVQZ256rm,\n    VPSLLVQZ256rmb,\n    VPSLLVQZ256rmbk,\n    VPSLLVQZ256rmbkz,\n    VPSLLVQZ256rmk,\n    VPSLLVQZ256rmkz,\n    VPSLLVQZrm,\n    VPSLLVQZrmb,\n    VPSLLVQZrmbk,\n    VPSLLVQZrmbkz,\n    VPSLLVQZrmk,\n    VPSLLVQZrmkz,\n    VPSLLVWZ128rm,\n    VPSLLVWZ128rmk,\n    VPSLLVWZ128rmkz,\n    VPSLLVWZ256rm,\n    VPSLLVWZ256rmk,\n    VPSLLVWZ256rmkz,\n    VPSLLVWZrm,\n    VPSLLVWZrmk,\n    VPSLLVWZrmkz,\n    VPSLLWZ128mi,\n    VPSLLWZ128mik,\n    VPSLLWZ128mikz,\n    VPSLLWZ128rm,\n    VPSLLWZ128rmk,\n    VPSLLWZ128rmkz,\n    VPSLLWZ256mi,\n    VPSLLWZ256mik,\n    VPSLLWZ256mikz,\n    VPSLLWZ256rm,\n    VPSLLWZ256rmk,\n    VPSLLWZ256rmkz,\n    VPSLLWZmi,\n    VPSLLWZmik,\n    VPSLLWZmikz,\n    VPSLLWZrm,\n    VPSLLWZrmk,\n    VPSLLWZrmkz,\n    VPSRADZ128mbi,\n    VPSRADZ128mbik,\n    VPSRADZ128mbikz,\n    VPSRADZ128mi,\n    VPSRADZ128mik,\n    VPSRADZ128mikz,\n    VPSRADZ128rm,\n    VPSRADZ128rmk,\n    VPSRADZ128rmkz,\n    VPSRADZ256mbi,\n    VPSRADZ256mbik,\n    VPSRADZ256mbikz,\n    VPSRADZ256mi,\n    VPSRADZ256mik,\n    VPSRADZ256mikz,\n    VPSRADZ256rm,\n    VPSRADZ256rmk,\n    VPSRADZ256rmkz,\n    VPSRADZmbi,\n    VPSRADZmbik,\n    VPSRADZmbikz,\n    VPSRADZmi,\n    VPSRADZmik,\n    VPSRADZmikz,\n    VPSRADZrm,\n    VPSRADZrmk,\n    VPSRADZrmkz,\n    VPSRAQZ128mbi,\n    VPSRAQZ128mbik,\n    VPSRAQZ128mbikz,\n    VPSRAQZ128mi,\n    VPSRAQZ128mik,\n    VPSRAQZ128mikz,\n    VPSRAQZ128rm,\n    VPSRAQZ128rmk,\n    VPSRAQZ128rmkz,\n    VPSRAQZ256mbi,\n    VPSRAQZ256mbik,\n    VPSRAQZ256mbikz,\n    VPSRAQZ256mi,\n    VPSRAQZ256mik,\n    VPSRAQZ256mikz,\n    VPSRAQZ256rm,\n    VPSRAQZ256rmk,\n    VPSRAQZ256rmkz,\n    VPSRAQZmbi,\n    VPSRAQZmbik,\n    VPSRAQZmbikz,\n    VPSRAQZmi,\n    VPSRAQZmik,\n    VPSRAQZmikz,\n    VPSRAQZrm,\n    VPSRAQZrmk,\n    VPSRAQZrmkz,\n    VPSRAVDZ128rm,\n    VPSRAVDZ128rmb,\n    VPSRAVDZ128rmbk,\n    VPSRAVDZ128rmbkz,\n    VPSRAVDZ128rmk,\n    VPSRAVDZ128rmkz,\n    VPSRAVDZ256rm,\n    VPSRAVDZ256rmb,\n    VPSRAVDZ256rmbk,\n    VPSRAVDZ256rmbkz,\n    VPSRAVDZ256rmk,\n    VPSRAVDZ256rmkz,\n    VPSRAVDZrm,\n    VPSRAVDZrmb,\n    VPSRAVDZrmbk,\n    VPSRAVDZrmbkz,\n    VPSRAVDZrmk,\n    VPSRAVDZrmkz,\n    VPSRAVQZ128rm,\n    VPSRAVQZ128rmb,\n    VPSRAVQZ128rmbk,\n    VPSRAVQZ128rmbkz,\n    VPSRAVQZ128rmk,\n    VPSRAVQZ128rmkz,\n    VPSRAVQZ256rm,\n    VPSRAVQZ256rmb,\n    VPSRAVQZ256rmbk,\n    VPSRAVQZ256rmbkz,\n    VPSRAVQZ256rmk,\n    VPSRAVQZ256rmkz,\n    VPSRAVQZrm,\n    VPSRAVQZrmb,\n    VPSRAVQZrmbk,\n    VPSRAVQZrmbkz,\n    VPSRAVQZrmk,\n    VPSRAVQZrmkz,\n    VPSRAVWZ128rm,\n    VPSRAVWZ128rmk,\n    VPSRAVWZ128rmkz,\n    VPSRAVWZ256rm,\n    VPSRAVWZ256rmk,\n    VPSRAVWZ256rmkz,\n    VPSRAVWZrm,\n    VPSRAVWZrmk,\n    VPSRAVWZrmkz,\n    VPSRAWZ128mi,\n    VPSRAWZ128mik,\n    VPSRAWZ128mikz,\n    VPSRAWZ128rm,\n    VPSRAWZ128rmk,\n    VPSRAWZ128rmkz,\n    VPSRAWZ256mi,\n    VPSRAWZ256mik,\n    VPSRAWZ256mikz,\n    VPSRAWZ256rm,\n    VPSRAWZ256rmk,\n    VPSRAWZ256rmkz,\n    VPSRAWZmi,\n    VPSRAWZmik,\n    VPSRAWZmikz,\n    VPSRAWZrm,\n    VPSRAWZrmk,\n    VPSRAWZrmkz,\n    VPSRLDQZ128mi,\n    VPSRLDQZ256mi,\n    VPSRLDQZmi,\n    VPSRLDZ128mbi,\n    VPSRLDZ128mbik,\n    VPSRLDZ128mbikz,\n    VPSRLDZ128mi,\n    VPSRLDZ128mik,\n    VPSRLDZ128mikz,\n    VPSRLDZ128rm,\n    VPSRLDZ128rmk,\n    VPSRLDZ128rmkz,\n    VPSRLDZ256mbi,\n    VPSRLDZ256mbik,\n    VPSRLDZ256mbikz,\n    VPSRLDZ256mi,\n    VPSRLDZ256mik,\n    VPSRLDZ256mikz,\n    VPSRLDZ256rm,\n    VPSRLDZ256rmk,\n    VPSRLDZ256rmkz,\n    VPSRLDZmbi,\n    VPSRLDZmbik,\n    VPSRLDZmbikz,\n    VPSRLDZmi,\n    VPSRLDZmik,\n    VPSRLDZmikz,\n    VPSRLDZrm,\n    VPSRLDZrmk,\n    VPSRLDZrmkz,\n    VPSRLQZ128mbi,\n    VPSRLQZ128mbik,\n    VPSRLQZ128mbikz,\n    VPSRLQZ128mi,\n    VPSRLQZ128mik,\n    VPSRLQZ128mikz,\n    VPSRLQZ128rm,\n    VPSRLQZ128rmk,\n    VPSRLQZ128rmkz,\n    VPSRLQZ256mbi,\n    VPSRLQZ256mbik,\n    VPSRLQZ256mbikz,\n    VPSRLQZ256mi,\n    VPSRLQZ256mik,\n    VPSRLQZ256mikz,\n    VPSRLQZ256rm,\n    VPSRLQZ256rmk,\n    VPSRLQZ256rmkz,\n    VPSRLQZmbi,\n    VPSRLQZmbik,\n    VPSRLQZmbikz,\n    VPSRLQZmi,\n    VPSRLQZmik,\n    VPSRLQZmikz,\n    VPSRLQZrm,\n    VPSRLQZrmk,\n    VPSRLQZrmkz,\n    VPSRLVDZ128rm,\n    VPSRLVDZ128rmb,\n    VPSRLVDZ128rmbk,\n    VPSRLVDZ128rmbkz,\n    VPSRLVDZ128rmk,\n    VPSRLVDZ128rmkz,\n    VPSRLVDZ256rm,\n    VPSRLVDZ256rmb,\n    VPSRLVDZ256rmbk,\n    VPSRLVDZ256rmbkz,\n    VPSRLVDZ256rmk,\n    VPSRLVDZ256rmkz,\n    VPSRLVDZrm,\n    VPSRLVDZrmb,\n    VPSRLVDZrmbk,\n    VPSRLVDZrmbkz,\n    VPSRLVDZrmk,\n    VPSRLVDZrmkz,\n    VPSRLVQZ128rm,\n    VPSRLVQZ128rmb,\n    VPSRLVQZ128rmbk,\n    VPSRLVQZ128rmbkz,\n    VPSRLVQZ128rmk,\n    VPSRLVQZ128rmkz,\n    VPSRLVQZ256rm,\n    VPSRLVQZ256rmb,\n    VPSRLVQZ256rmbk,\n    VPSRLVQZ256rmbkz,\n    VPSRLVQZ256rmk,\n    VPSRLVQZ256rmkz,\n    VPSRLVQZrm,\n    VPSRLVQZrmb,\n    VPSRLVQZrmbk,\n    VPSRLVQZrmbkz,\n    VPSRLVQZrmk,\n    VPSRLVQZrmkz,\n    VPSRLVWZ128rm,\n    VPSRLVWZ128rmk,\n    VPSRLVWZ128rmkz,\n    VPSRLVWZ256rm,\n    VPSRLVWZ256rmk,\n    VPSRLVWZ256rmkz,\n    VPSRLVWZrm,\n    VPSRLVWZrmk,\n    VPSRLVWZrmkz,\n    VPSRLWZ128mi,\n    VPSRLWZ128mik,\n    VPSRLWZ128mikz,\n    VPSRLWZ128rm,\n    VPSRLWZ128rmk,\n    VPSRLWZ128rmkz,\n    VPSRLWZ256mi,\n    VPSRLWZ256mik,\n    VPSRLWZ256mikz,\n    VPSRLWZ256rm,\n    VPSRLWZ256rmk,\n    VPSRLWZ256rmkz,\n    VPSRLWZmi,\n    VPSRLWZmik,\n    VPSRLWZmikz,\n    VPSRLWZrm,\n    VPSRLWZrmk,\n    VPSRLWZrmkz,\n    VPSUBBZ128rm,\n    VPSUBBZ128rmk,\n    VPSUBBZ128rmkz,\n    VPSUBBZ256rm,\n    VPSUBBZ256rmk,\n    VPSUBBZ256rmkz,\n    VPSUBBZrm,\n    VPSUBBZrmk,\n    VPSUBBZrmkz,\n    VPSUBDZ128rm,\n    VPSUBDZ128rmb,\n    VPSUBDZ128rmbk,\n    VPSUBDZ128rmbkz,\n    VPSUBDZ128rmk,\n    VPSUBDZ128rmkz,\n    VPSUBDZ256rm,\n    VPSUBDZ256rmb,\n    VPSUBDZ256rmbk,\n    VPSUBDZ256rmbkz,\n    VPSUBDZ256rmk,\n    VPSUBDZ256rmkz,\n    VPSUBDZrm,\n    VPSUBDZrmb,\n    VPSUBDZrmbk,\n    VPSUBDZrmbkz,\n    VPSUBDZrmk,\n    VPSUBDZrmkz,\n    VPSUBQZ128rm,\n    VPSUBQZ128rmb,\n    VPSUBQZ128rmbk,\n    VPSUBQZ128rmbkz,\n    VPSUBQZ128rmk,\n    VPSUBQZ128rmkz,\n    VPSUBQZ256rm,\n    VPSUBQZ256rmb,\n    VPSUBQZ256rmbk,\n    VPSUBQZ256rmbkz,\n    VPSUBQZ256rmk,\n    VPSUBQZ256rmkz,\n    VPSUBQZrm,\n    VPSUBQZrmb,\n    VPSUBQZrmbk,\n    VPSUBQZrmbkz,\n    VPSUBQZrmk,\n    VPSUBQZrmkz,\n    VPSUBSBZ128rm,\n    VPSUBSBZ128rmk,\n    VPSUBSBZ128rmkz,\n    VPSUBSBZ256rm,\n    VPSUBSBZ256rmk,\n    VPSUBSBZ256rmkz,\n    VPSUBSBZrm,\n    VPSUBSBZrmk,\n    VPSUBSBZrmkz,\n    VPSUBSWZ128rm,\n    VPSUBSWZ128rmk,\n    VPSUBSWZ128rmkz,\n    VPSUBSWZ256rm,\n    VPSUBSWZ256rmk,\n    VPSUBSWZ256rmkz,\n    VPSUBSWZrm,\n    VPSUBSWZrmk,\n    VPSUBSWZrmkz,\n    VPSUBUSBZ128rm,\n    VPSUBUSBZ128rmk,\n    VPSUBUSBZ128rmkz,\n    VPSUBUSBZ256rm,\n    VPSUBUSBZ256rmk,\n    VPSUBUSBZ256rmkz,\n    VPSUBUSBZrm,\n    VPSUBUSBZrmk,\n    VPSUBUSBZrmkz,\n    VPSUBUSWZ128rm,\n    VPSUBUSWZ128rmk,\n    VPSUBUSWZ128rmkz,\n    VPSUBUSWZ256rm,\n    VPSUBUSWZ256rmk,\n    VPSUBUSWZ256rmkz,\n    VPSUBUSWZrm,\n    VPSUBUSWZrmk,\n    VPSUBUSWZrmkz,\n    VPSUBWZ128rm,\n    VPSUBWZ128rmk,\n    VPSUBWZ128rmkz,\n    VPSUBWZ256rm,\n    VPSUBWZ256rmk,\n    VPSUBWZ256rmkz,\n    VPSUBWZrm,\n    VPSUBWZrmk,\n    VPSUBWZrmkz,\n    VPTERNLOGDZ128rmbi,\n    VPTERNLOGDZ128rmbik,\n    VPTERNLOGDZ128rmbikz,\n    VPTERNLOGDZ128rmi,\n    VPTERNLOGDZ128rmik,\n    VPTERNLOGDZ128rmikz,\n    VPTERNLOGDZ256rmbi,\n    VPTERNLOGDZ256rmbik,\n    VPTERNLOGDZ256rmbikz,\n    VPTERNLOGDZ256rmi,\n    VPTERNLOGDZ256rmik,\n    VPTERNLOGDZ256rmikz,\n    VPTERNLOGDZrmbi,\n    VPTERNLOGDZrmbik,\n    VPTERNLOGDZrmbikz,\n    VPTERNLOGDZrmi,\n    VPTERNLOGDZrmik,\n    VPTERNLOGDZrmikz,\n    VPTERNLOGQZ128rmbi,\n    VPTERNLOGQZ128rmbik,\n    VPTERNLOGQZ128rmbikz,\n    VPTERNLOGQZ128rmi,\n    VPTERNLOGQZ128rmik,\n    VPTERNLOGQZ128rmikz,\n    VPTERNLOGQZ256rmbi,\n    VPTERNLOGQZ256rmbik,\n    VPTERNLOGQZ256rmbikz,\n    VPTERNLOGQZ256rmi,\n    VPTERNLOGQZ256rmik,\n    VPTERNLOGQZ256rmikz,\n    VPTERNLOGQZrmbi,\n    VPTERNLOGQZrmbik,\n    VPTERNLOGQZrmbikz,\n    VPTERNLOGQZrmi,\n    VPTERNLOGQZrmik,\n    VPTERNLOGQZrmikz,\n    VPTESTMBZ128rm,\n    VPTESTMBZ128rmk,\n    VPTESTMBZ256rm,\n    VPTESTMBZ256rmk,\n    VPTESTMBZrm,\n    VPTESTMBZrmk,\n    VPTESTMDZ128rm,\n    VPTESTMDZ128rmb,\n    VPTESTMDZ128rmbk,\n    VPTESTMDZ128rmk,\n    VPTESTMDZ256rm,\n    VPTESTMDZ256rmb,\n    VPTESTMDZ256rmbk,\n    VPTESTMDZ256rmk,\n    VPTESTMDZrm,\n    VPTESTMDZrmb,\n    VPTESTMDZrmbk,\n    VPTESTMDZrmk,\n    VPTESTMQZ128rm,\n    VPTESTMQZ128rmb,\n    VPTESTMQZ128rmbk,\n    VPTESTMQZ128rmk,\n    VPTESTMQZ256rm,\n    VPTESTMQZ256rmb,\n    VPTESTMQZ256rmbk,\n    VPTESTMQZ256rmk,\n    VPTESTMQZrm,\n    VPTESTMQZrmb,\n    VPTESTMQZrmbk,\n    VPTESTMQZrmk,\n    VPTESTMWZ128rm,\n    VPTESTMWZ128rmk,\n    VPTESTMWZ256rm,\n    VPTESTMWZ256rmk,\n    VPTESTMWZrm,\n    VPTESTMWZrmk,\n    VPTESTNMBZ128rm,\n    VPTESTNMBZ128rmk,\n    VPTESTNMBZ256rm,\n    VPTESTNMBZ256rmk,\n    VPTESTNMBZrm,\n    VPTESTNMBZrmk,\n    VPTESTNMDZ128rm,\n    VPTESTNMDZ128rmb,\n    VPTESTNMDZ128rmbk,\n    VPTESTNMDZ128rmk,\n    VPTESTNMDZ256rm,\n    VPTESTNMDZ256rmb,\n    VPTESTNMDZ256rmbk,\n    VPTESTNMDZ256rmk,\n    VPTESTNMDZrm,\n    VPTESTNMDZrmb,\n    VPTESTNMDZrmbk,\n    VPTESTNMDZrmk,\n    VPTESTNMQZ128rm,\n    VPTESTNMQZ128rmb,\n    VPTESTNMQZ128rmbk,\n    VPTESTNMQZ128rmk,\n    VPTESTNMQZ256rm,\n    VPTESTNMQZ256rmb,\n    VPTESTNMQZ256rmbk,\n    VPTESTNMQZ256rmk,\n    VPTESTNMQZrm,\n    VPTESTNMQZrmb,\n    VPTESTNMQZrmbk,\n    VPTESTNMQZrmk,\n    VPTESTNMWZ128rm,\n    VPTESTNMWZ128rmk,\n    VPTESTNMWZ256rm,\n    VPTESTNMWZ256rmk,\n    VPTESTNMWZrm,\n    VPTESTNMWZrmk,\n    VPUNPCKHBWZ128rm,\n    VPUNPCKHBWZ128rmk,\n    VPUNPCKHBWZ128rmkz,\n    VPUNPCKHBWZ256rm,\n    VPUNPCKHBWZ256rmk,\n    VPUNPCKHBWZ256rmkz,\n    VPUNPCKHBWZrm,\n    VPUNPCKHBWZrmk,\n    VPUNPCKHBWZrmkz,\n    VPUNPCKHDQZ128rm,\n    VPUNPCKHDQZ128rmb,\n    VPUNPCKHDQZ128rmbk,\n    VPUNPCKHDQZ128rmbkz,\n    VPUNPCKHDQZ128rmk,\n    VPUNPCKHDQZ128rmkz,\n    VPUNPCKHDQZ256rm,\n    VPUNPCKHDQZ256rmb,\n    VPUNPCKHDQZ256rmbk,\n    VPUNPCKHDQZ256rmbkz,\n    VPUNPCKHDQZ256rmk,\n    VPUNPCKHDQZ256rmkz,\n    VPUNPCKHDQZrm,\n    VPUNPCKHDQZrmb,\n    VPUNPCKHDQZrmbk,\n    VPUNPCKHDQZrmbkz,\n    VPUNPCKHDQZrmk,\n    VPUNPCKHDQZrmkz,\n    VPUNPCKHQDQZ128rm,\n    VPUNPCKHQDQZ128rmb,\n    VPUNPCKHQDQZ128rmbk,\n    VPUNPCKHQDQZ128rmbkz,\n    VPUNPCKHQDQZ128rmk,\n    VPUNPCKHQDQZ128rmkz,\n    VPUNPCKHQDQZ256rm,\n    VPUNPCKHQDQZ256rmb,\n    VPUNPCKHQDQZ256rmbk,\n    VPUNPCKHQDQZ256rmbkz,\n    VPUNPCKHQDQZ256rmk,\n    VPUNPCKHQDQZ256rmkz,\n    VPUNPCKHQDQZrm,\n    VPUNPCKHQDQZrmb,\n    VPUNPCKHQDQZrmbk,\n    VPUNPCKHQDQZrmbkz,\n    VPUNPCKHQDQZrmk,\n    VPUNPCKHQDQZrmkz,\n    VPUNPCKHWDZ128rm,\n    VPUNPCKHWDZ128rmk,\n    VPUNPCKHWDZ128rmkz,\n    VPUNPCKHWDZ256rm,\n    VPUNPCKHWDZ256rmk,\n    VPUNPCKHWDZ256rmkz,\n    VPUNPCKHWDZrm,\n    VPUNPCKHWDZrmk,\n    VPUNPCKHWDZrmkz,\n    VPUNPCKLBWZ128rm,\n    VPUNPCKLBWZ128rmk,\n    VPUNPCKLBWZ128rmkz,\n    VPUNPCKLBWZ256rm,\n    VPUNPCKLBWZ256rmk,\n    VPUNPCKLBWZ256rmkz,\n    VPUNPCKLBWZrm,\n    VPUNPCKLBWZrmk,\n    VPUNPCKLBWZrmkz,\n    VPUNPCKLDQZ128rm,\n    VPUNPCKLDQZ128rmb,\n    VPUNPCKLDQZ128rmbk,\n    VPUNPCKLDQZ128rmbkz,\n    VPUNPCKLDQZ128rmk,\n    VPUNPCKLDQZ128rmkz,\n    VPUNPCKLDQZ256rm,\n    VPUNPCKLDQZ256rmb,\n    VPUNPCKLDQZ256rmbk,\n    VPUNPCKLDQZ256rmbkz,\n    VPUNPCKLDQZ256rmk,\n    VPUNPCKLDQZ256rmkz,\n    VPUNPCKLDQZrm,\n    VPUNPCKLDQZrmb,\n    VPUNPCKLDQZrmbk,\n    VPUNPCKLDQZrmbkz,\n    VPUNPCKLDQZrmk,\n    VPUNPCKLDQZrmkz,\n    VPUNPCKLQDQZ128rm,\n    VPUNPCKLQDQZ128rmb,\n    VPUNPCKLQDQZ128rmbk,\n    VPUNPCKLQDQZ128rmbkz,\n    VPUNPCKLQDQZ128rmk,\n    VPUNPCKLQDQZ128rmkz,\n    VPUNPCKLQDQZ256rm,\n    VPUNPCKLQDQZ256rmb,\n    VPUNPCKLQDQZ256rmbk,\n    VPUNPCKLQDQZ256rmbkz,\n    VPUNPCKLQDQZ256rmk,\n    VPUNPCKLQDQZ256rmkz,\n    VPUNPCKLQDQZrm,\n    VPUNPCKLQDQZrmb,\n    VPUNPCKLQDQZrmbk,\n    VPUNPCKLQDQZrmbkz,\n    VPUNPCKLQDQZrmk,\n    VPUNPCKLQDQZrmkz,\n    VPUNPCKLWDZ128rm,\n    VPUNPCKLWDZ128rmk,\n    VPUNPCKLWDZ128rmkz,\n    VPUNPCKLWDZ256rm,\n    VPUNPCKLWDZ256rmk,\n    VPUNPCKLWDZ256rmkz,\n    VPUNPCKLWDZrm,\n    VPUNPCKLWDZrmk,\n    VPUNPCKLWDZrmkz,\n    VPXORDZ128rm,\n    VPXORDZ128rmb,\n    VPXORDZ128rmbk,\n    VPXORDZ128rmbkz,\n    VPXORDZ128rmk,\n    VPXORDZ128rmkz,\n    VPXORDZ256rm,\n    VPXORDZ256rmb,\n    VPXORDZ256rmbk,\n    VPXORDZ256rmbkz,\n    VPXORDZ256rmk,\n    VPXORDZ256rmkz,\n    VPXORDZrm,\n    VPXORDZrmb,\n    VPXORDZrmbk,\n    VPXORDZrmbkz,\n    VPXORDZrmk,\n    VPXORDZrmkz,\n    VPXORQZ128rm,\n    VPXORQZ128rmb,\n    VPXORQZ128rmbk,\n    VPXORQZ128rmbkz,\n    VPXORQZ128rmk,\n    VPXORQZ128rmkz,\n    VPXORQZ256rm,\n    VPXORQZ256rmb,\n    VPXORQZ256rmbk,\n    VPXORQZ256rmbkz,\n    VPXORQZ256rmk,\n    VPXORQZ256rmkz,\n    VPXORQZrm,\n    VPXORQZrmb,\n    VPXORQZrmbk,\n    VPXORQZrmbkz,\n    VPXORQZrmk,\n    VPXORQZrmkz,\n    VRANGEPDZ128rmbi,\n    VRANGEPDZ128rmbik,\n    VRANGEPDZ128rmbikz,\n    VRANGEPDZ128rmi,\n    VRANGEPDZ128rmik,\n    VRANGEPDZ128rmikz,\n    VRANGEPDZ256rmbi,\n    VRANGEPDZ256rmbik,\n    VRANGEPDZ256rmbikz,\n    VRANGEPDZ256rmi,\n    VRANGEPDZ256rmik,\n    VRANGEPDZ256rmikz,\n    VRANGEPDZrmbi,\n    VRANGEPDZrmbik,\n    VRANGEPDZrmbikz,\n    VRANGEPDZrmi,\n    VRANGEPDZrmik,\n    VRANGEPDZrmikz,\n    VRANGEPSZ128rmbi,\n    VRANGEPSZ128rmbik,\n    VRANGEPSZ128rmbikz,\n    VRANGEPSZ128rmi,\n    VRANGEPSZ128rmik,\n    VRANGEPSZ128rmikz,\n    VRANGEPSZ256rmbi,\n    VRANGEPSZ256rmbik,\n    VRANGEPSZ256rmbikz,\n    VRANGEPSZ256rmi,\n    VRANGEPSZ256rmik,\n    VRANGEPSZ256rmikz,\n    VRANGEPSZrmbi,\n    VRANGEPSZrmbik,\n    VRANGEPSZrmbikz,\n    VRANGEPSZrmi,\n    VRANGEPSZrmik,\n    VRANGEPSZrmikz,\n    VRANGESDZrmi,\n    VRANGESDZrmik,\n    VRANGESDZrmikz,\n    VRANGESSZrmi,\n    VRANGESSZrmik,\n    VRANGESSZrmikz,\n    VRCP14PDZ128m,\n    VRCP14PDZ128mb,\n    VRCP14PDZ128mbk,\n    VRCP14PDZ128mbkz,\n    VRCP14PDZ128mk,\n    VRCP14PDZ128mkz,\n    VRCP14PDZ256m,\n    VRCP14PDZ256mb,\n    VRCP14PDZ256mbk,\n    VRCP14PDZ256mbkz,\n    VRCP14PDZ256mk,\n    VRCP14PDZ256mkz,\n    VRCP14PDZm,\n    VRCP14PDZmb,\n    VRCP14PDZmbk,\n    VRCP14PDZmbkz,\n    VRCP14PDZmk,\n    VRCP14PDZmkz,\n    VRCP14PSZ128m,\n    VRCP14PSZ128mb,\n    VRCP14PSZ128mbk,\n    VRCP14PSZ128mbkz,\n    VRCP14PSZ128mk,\n    VRCP14PSZ128mkz,\n    VRCP14PSZ256m,\n    VRCP14PSZ256mb,\n    VRCP14PSZ256mbk,\n    VRCP14PSZ256mbkz,\n    VRCP14PSZ256mk,\n    VRCP14PSZ256mkz,\n    VRCP14PSZm,\n    VRCP14PSZmb,\n    VRCP14PSZmbk,\n    VRCP14PSZmbkz,\n    VRCP14PSZmk,\n    VRCP14PSZmkz,\n    VRCP14SDZrm,\n    VRCP14SDZrmk,\n    VRCP14SDZrmkz,\n    VRCP14SSZrm,\n    VRCP14SSZrmk,\n    VRCP14SSZrmkz,\n    VRCP28PDZm,\n    VRCP28PDZmb,\n    VRCP28PDZmbk,\n    VRCP28PDZmbkz,\n    VRCP28PDZmk,\n    VRCP28PDZmkz,\n    VRCP28PSZm,\n    VRCP28PSZmb,\n    VRCP28PSZmbk,\n    VRCP28PSZmbkz,\n    VRCP28PSZmk,\n    VRCP28PSZmkz,\n    VRCP28SDZm,\n    VRCP28SDZmk,\n    VRCP28SDZmkz,\n    VRCP28SSZm,\n    VRCP28SSZmk,\n    VRCP28SSZmkz,\n    VRCPPHZ128m,\n    VRCPPHZ128mb,\n    VRCPPHZ128mbk,\n    VRCPPHZ128mbkz,\n    VRCPPHZ128mk,\n    VRCPPHZ128mkz,\n    VRCPPHZ256m,\n    VRCPPHZ256mb,\n    VRCPPHZ256mbk,\n    VRCPPHZ256mbkz,\n    VRCPPHZ256mk,\n    VRCPPHZ256mkz,\n    VRCPPHZm,\n    VRCPPHZmb,\n    VRCPPHZmbk,\n    VRCPPHZmbkz,\n    VRCPPHZmk,\n    VRCPPHZmkz,\n    VRCPSHZrm,\n    VRCPSHZrmk,\n    VRCPSHZrmkz,\n    VREDUCEPDZ128rmbi,\n    VREDUCEPDZ128rmbik,\n    VREDUCEPDZ128rmbikz,\n    VREDUCEPDZ128rmi,\n    VREDUCEPDZ128rmik,\n    VREDUCEPDZ128rmikz,\n    VREDUCEPDZ256rmbi,\n    VREDUCEPDZ256rmbik,\n    VREDUCEPDZ256rmbikz,\n    VREDUCEPDZ256rmi,\n    VREDUCEPDZ256rmik,\n    VREDUCEPDZ256rmikz,\n    VREDUCEPDZrmbi,\n    VREDUCEPDZrmbik,\n    VREDUCEPDZrmbikz,\n    VREDUCEPDZrmi,\n    VREDUCEPDZrmik,\n    VREDUCEPDZrmikz,\n    VREDUCEPHZ128rmbi,\n    VREDUCEPHZ128rmbik,\n    VREDUCEPHZ128rmbikz,\n    VREDUCEPHZ128rmi,\n    VREDUCEPHZ128rmik,\n    VREDUCEPHZ128rmikz,\n    VREDUCEPHZ256rmbi,\n    VREDUCEPHZ256rmbik,\n    VREDUCEPHZ256rmbikz,\n    VREDUCEPHZ256rmi,\n    VREDUCEPHZ256rmik,\n    VREDUCEPHZ256rmikz,\n    VREDUCEPHZrmbi,\n    VREDUCEPHZrmbik,\n    VREDUCEPHZrmbikz,\n    VREDUCEPHZrmi,\n    VREDUCEPHZrmik,\n    VREDUCEPHZrmikz,\n    VREDUCEPSZ128rmbi,\n    VREDUCEPSZ128rmbik,\n    VREDUCEPSZ128rmbikz,\n    VREDUCEPSZ128rmi,\n    VREDUCEPSZ128rmik,\n    VREDUCEPSZ128rmikz,\n    VREDUCEPSZ256rmbi,\n    VREDUCEPSZ256rmbik,\n    VREDUCEPSZ256rmbikz,\n    VREDUCEPSZ256rmi,\n    VREDUCEPSZ256rmik,\n    VREDUCEPSZ256rmikz,\n    VREDUCEPSZrmbi,\n    VREDUCEPSZrmbik,\n    VREDUCEPSZrmbikz,\n    VREDUCEPSZrmi,\n    VREDUCEPSZrmik,\n    VREDUCEPSZrmikz,\n    VREDUCESDZrmi,\n    VREDUCESDZrmik,\n    VREDUCESDZrmikz,\n    VREDUCESHZrmi,\n    VREDUCESHZrmik,\n    VREDUCESHZrmikz,\n    VREDUCESSZrmi,\n    VREDUCESSZrmik,\n    VREDUCESSZrmikz,\n    VRNDSCALEPDZ128rmbi,\n    VRNDSCALEPDZ128rmbik,\n    VRNDSCALEPDZ128rmbikz,\n    VRNDSCALEPDZ128rmi,\n    VRNDSCALEPDZ128rmik,\n    VRNDSCALEPDZ128rmikz,\n    VRNDSCALEPDZ256rmbi,\n    VRNDSCALEPDZ256rmbik,\n    VRNDSCALEPDZ256rmbikz,\n    VRNDSCALEPDZ256rmi,\n    VRNDSCALEPDZ256rmik,\n    VRNDSCALEPDZ256rmikz,\n    VRNDSCALEPDZrmbi,\n    VRNDSCALEPDZrmbik,\n    VRNDSCALEPDZrmbikz,\n    VRNDSCALEPDZrmi,\n    VRNDSCALEPDZrmik,\n    VRNDSCALEPDZrmikz,\n    VRNDSCALEPHZ128rmbi,\n    VRNDSCALEPHZ128rmbik,\n    VRNDSCALEPHZ128rmbikz,\n    VRNDSCALEPHZ128rmi,\n    VRNDSCALEPHZ128rmik,\n    VRNDSCALEPHZ128rmikz,\n    VRNDSCALEPHZ256rmbi,\n    VRNDSCALEPHZ256rmbik,\n    VRNDSCALEPHZ256rmbikz,\n    VRNDSCALEPHZ256rmi,\n    VRNDSCALEPHZ256rmik,\n    VRNDSCALEPHZ256rmikz,\n    VRNDSCALEPHZrmbi,\n    VRNDSCALEPHZrmbik,\n    VRNDSCALEPHZrmbikz,\n    VRNDSCALEPHZrmi,\n    VRNDSCALEPHZrmik,\n    VRNDSCALEPHZrmikz,\n    VRNDSCALEPSZ128rmbi,\n    VRNDSCALEPSZ128rmbik,\n    VRNDSCALEPSZ128rmbikz,\n    VRNDSCALEPSZ128rmi,\n    VRNDSCALEPSZ128rmik,\n    VRNDSCALEPSZ128rmikz,\n    VRNDSCALEPSZ256rmbi,\n    VRNDSCALEPSZ256rmbik,\n    VRNDSCALEPSZ256rmbikz,\n    VRNDSCALEPSZ256rmi,\n    VRNDSCALEPSZ256rmik,\n    VRNDSCALEPSZ256rmikz,\n    VRNDSCALEPSZrmbi,\n    VRNDSCALEPSZrmbik,\n    VRNDSCALEPSZrmbikz,\n    VRNDSCALEPSZrmi,\n    VRNDSCALEPSZrmik,\n    VRNDSCALEPSZrmikz,\n    VRNDSCALESDZm,\n    VRNDSCALESDZm_Int,\n    VRNDSCALESDZm_Intk,\n    VRNDSCALESDZm_Intkz,\n    VRNDSCALESHZm,\n    VRNDSCALESHZm_Int,\n    VRNDSCALESHZm_Intk,\n    VRNDSCALESHZm_Intkz,\n    VRNDSCALESSZm,\n    VRNDSCALESSZm_Int,\n    VRNDSCALESSZm_Intk,\n    VRNDSCALESSZm_Intkz,\n    VRSQRT14PDZ128m,\n    VRSQRT14PDZ128mb,\n    VRSQRT14PDZ128mbk,\n    VRSQRT14PDZ128mbkz,\n    VRSQRT14PDZ128mk,\n    VRSQRT14PDZ128mkz,\n    VRSQRT14PDZ256m,\n    VRSQRT14PDZ256mb,\n    VRSQRT14PDZ256mbk,\n    VRSQRT14PDZ256mbkz,\n    VRSQRT14PDZ256mk,\n    VRSQRT14PDZ256mkz,\n    VRSQRT14PDZm,\n    VRSQRT14PDZmb,\n    VRSQRT14PDZmbk,\n    VRSQRT14PDZmbkz,\n    VRSQRT14PDZmk,\n    VRSQRT14PDZmkz,\n    VRSQRT14PSZ128m,\n    VRSQRT14PSZ128mb,\n    VRSQRT14PSZ128mbk,\n    VRSQRT14PSZ128mbkz,\n    VRSQRT14PSZ128mk,\n    VRSQRT14PSZ128mkz,\n    VRSQRT14PSZ256m,\n    VRSQRT14PSZ256mb,\n    VRSQRT14PSZ256mbk,\n    VRSQRT14PSZ256mbkz,\n    VRSQRT14PSZ256mk,\n    VRSQRT14PSZ256mkz,\n    VRSQRT14PSZm,\n    VRSQRT14PSZmb,\n    VRSQRT14PSZmbk,\n    VRSQRT14PSZmbkz,\n    VRSQRT14PSZmk,\n    VRSQRT14PSZmkz,\n    VRSQRT14SDZrm,\n    VRSQRT14SDZrmk,\n    VRSQRT14SDZrmkz,\n    VRSQRT14SSZrm,\n    VRSQRT14SSZrmk,\n    VRSQRT14SSZrmkz,\n    VRSQRT28PDZm,\n    VRSQRT28PDZmb,\n    VRSQRT28PDZmbk,\n    VRSQRT28PDZmbkz,\n    VRSQRT28PDZmk,\n    VRSQRT28PDZmkz,\n    VRSQRT28PSZm,\n    VRSQRT28PSZmb,\n    VRSQRT28PSZmbk,\n    VRSQRT28PSZmbkz,\n    VRSQRT28PSZmk,\n    VRSQRT28PSZmkz,\n    VRSQRT28SDZm,\n    VRSQRT28SDZmk,\n    VRSQRT28SDZmkz,\n    VRSQRT28SSZm,\n    VRSQRT28SSZmk,\n    VRSQRT28SSZmkz,\n    VRSQRTPHZ128m,\n    VRSQRTPHZ128mb,\n    VRSQRTPHZ128mbk,\n    VRSQRTPHZ128mbkz,\n    VRSQRTPHZ128mk,\n    VRSQRTPHZ128mkz,\n    VRSQRTPHZ256m,\n    VRSQRTPHZ256mb,\n    VRSQRTPHZ256mbk,\n    VRSQRTPHZ256mbkz,\n    VRSQRTPHZ256mk,\n    VRSQRTPHZ256mkz,\n    VRSQRTPHZm,\n    VRSQRTPHZmb,\n    VRSQRTPHZmbk,\n    VRSQRTPHZmbkz,\n    VRSQRTPHZmk,\n    VRSQRTPHZmkz,\n    VRSQRTSHZrm,\n    VRSQRTSHZrmk,\n    VRSQRTSHZrmkz,\n    VSCALEFPDZ128rm,\n    VSCALEFPDZ128rmb,\n    VSCALEFPDZ128rmbk,\n    VSCALEFPDZ128rmbkz,\n    VSCALEFPDZ128rmk,\n    VSCALEFPDZ128rmkz,\n    VSCALEFPDZ256rm,\n    VSCALEFPDZ256rmb,\n    VSCALEFPDZ256rmbk,\n    VSCALEFPDZ256rmbkz,\n    VSCALEFPDZ256rmk,\n    VSCALEFPDZ256rmkz,\n    VSCALEFPDZrm,\n    VSCALEFPDZrmb,\n    VSCALEFPDZrmbk,\n    VSCALEFPDZrmbkz,\n    VSCALEFPDZrmk,\n    VSCALEFPDZrmkz,\n    VSCALEFPHZ128rm,\n    VSCALEFPHZ128rmb,\n    VSCALEFPHZ128rmbk,\n    VSCALEFPHZ128rmbkz,\n    VSCALEFPHZ128rmk,\n    VSCALEFPHZ128rmkz,\n    VSCALEFPHZ256rm,\n    VSCALEFPHZ256rmb,\n    VSCALEFPHZ256rmbk,\n    VSCALEFPHZ256rmbkz,\n    VSCALEFPHZ256rmk,\n    VSCALEFPHZ256rmkz,\n    VSCALEFPHZrm,\n    VSCALEFPHZrmb,\n    VSCALEFPHZrmbk,\n    VSCALEFPHZrmbkz,\n    VSCALEFPHZrmk,\n    VSCALEFPHZrmkz,\n    VSCALEFPSZ128rm,\n    VSCALEFPSZ128rmb,\n    VSCALEFPSZ128rmbk,\n    VSCALEFPSZ128rmbkz,\n    VSCALEFPSZ128rmk,\n    VSCALEFPSZ128rmkz,\n    VSCALEFPSZ256rm,\n    VSCALEFPSZ256rmb,\n    VSCALEFPSZ256rmbk,\n    VSCALEFPSZ256rmbkz,\n    VSCALEFPSZ256rmk,\n    VSCALEFPSZ256rmkz,\n    VSCALEFPSZrm,\n    VSCALEFPSZrmb,\n    VSCALEFPSZrmbk,\n    VSCALEFPSZrmbkz,\n    VSCALEFPSZrmk,\n    VSCALEFPSZrmkz,\n    VSCALEFSDZrm,\n    VSCALEFSDZrmk,\n    VSCALEFSDZrmkz,\n    VSCALEFSHZrm,\n    VSCALEFSHZrmk,\n    VSCALEFSHZrmkz,\n    VSCALEFSSZrm,\n    VSCALEFSSZrmk,\n    VSCALEFSSZrmkz,\n    VSCATTERDPDZ128mr,\n    VSCATTERDPDZ256mr,\n    VSCATTERDPDZmr,\n    VSCATTERDPSZ128mr,\n    VSCATTERDPSZ256mr,\n    VSCATTERDPSZmr,\n    VSCATTERPF0DPDm,\n    VSCATTERPF0DPSm,\n    VSCATTERPF0QPDm,\n    VSCATTERPF0QPSm,\n    VSCATTERPF1DPDm,\n    VSCATTERPF1DPSm,\n    VSCATTERPF1QPDm,\n    VSCATTERPF1QPSm,\n    VSCATTERQPDZ128mr,\n    VSCATTERQPDZ256mr,\n    VSCATTERQPDZmr,\n    VSCATTERQPSZ128mr,\n    VSCATTERQPSZ256mr,\n    VSCATTERQPSZmr,\n    VSHUFF32X4Z256rmbi,\n    VSHUFF32X4Z256rmbik,\n    VSHUFF32X4Z256rmbikz,\n    VSHUFF32X4Z256rmi,\n    VSHUFF32X4Z256rmik,\n    VSHUFF32X4Z256rmikz,\n    VSHUFF32X4Zrmbi,\n    VSHUFF32X4Zrmbik,\n    VSHUFF32X4Zrmbikz,\n    VSHUFF32X4Zrmi,\n    VSHUFF32X4Zrmik,\n    VSHUFF32X4Zrmikz,\n    VSHUFF64X2Z256rmbi,\n    VSHUFF64X2Z256rmbik,\n    VSHUFF64X2Z256rmbikz,\n    VSHUFF64X2Z256rmi,\n    VSHUFF64X2Z256rmik,\n    VSHUFF64X2Z256rmikz,\n    VSHUFF64X2Zrmbi,\n    VSHUFF64X2Zrmbik,\n    VSHUFF64X2Zrmbikz,\n    VSHUFF64X2Zrmi,\n    VSHUFF64X2Zrmik,\n    VSHUFF64X2Zrmikz,\n    VSHUFI32X4Z256rmbi,\n    VSHUFI32X4Z256rmbik,\n    VSHUFI32X4Z256rmbikz,\n    VSHUFI32X4Z256rmi,\n    VSHUFI32X4Z256rmik,\n    VSHUFI32X4Z256rmikz,\n    VSHUFI32X4Zrmbi,\n    VSHUFI32X4Zrmbik,\n    VSHUFI32X4Zrmbikz,\n    VSHUFI32X4Zrmi,\n    VSHUFI32X4Zrmik,\n    VSHUFI32X4Zrmikz,\n    VSHUFI64X2Z256rmbi,\n    VSHUFI64X2Z256rmbik,\n    VSHUFI64X2Z256rmbikz,\n    VSHUFI64X2Z256rmi,\n    VSHUFI64X2Z256rmik,\n    VSHUFI64X2Z256rmikz,\n    VSHUFI64X2Zrmbi,\n    VSHUFI64X2Zrmbik,\n    VSHUFI64X2Zrmbikz,\n    VSHUFI64X2Zrmi,\n    VSHUFI64X2Zrmik,\n    VSHUFI64X2Zrmikz,\n    VSHUFPDZ128rmbi,\n    VSHUFPDZ128rmbik,\n    VSHUFPDZ128rmbikz,\n    VSHUFPDZ128rmi,\n    VSHUFPDZ128rmik,\n    VSHUFPDZ128rmikz,\n    VSHUFPDZ256rmbi,\n    VSHUFPDZ256rmbik,\n    VSHUFPDZ256rmbikz,\n    VSHUFPDZ256rmi,\n    VSHUFPDZ256rmik,\n    VSHUFPDZ256rmikz,\n    VSHUFPDZrmbi,\n    VSHUFPDZrmbik,\n    VSHUFPDZrmbikz,\n    VSHUFPDZrmi,\n    VSHUFPDZrmik,\n    VSHUFPDZrmikz,\n    VSHUFPSZ128rmbi,\n    VSHUFPSZ128rmbik,\n    VSHUFPSZ128rmbikz,\n    VSHUFPSZ128rmi,\n    VSHUFPSZ128rmik,\n    VSHUFPSZ128rmikz,\n    VSHUFPSZ256rmbi,\n    VSHUFPSZ256rmbik,\n    VSHUFPSZ256rmbikz,\n    VSHUFPSZ256rmi,\n    VSHUFPSZ256rmik,\n    VSHUFPSZ256rmikz,\n    VSHUFPSZrmbi,\n    VSHUFPSZrmbik,\n    VSHUFPSZrmbikz,\n    VSHUFPSZrmi,\n    VSHUFPSZrmik,\n    VSHUFPSZrmikz,\n    VSQRTPDZ128m,\n    VSQRTPDZ128mb,\n    VSQRTPDZ128mbk,\n    VSQRTPDZ128mbkz,\n    VSQRTPDZ128mk,\n    VSQRTPDZ128mkz,\n    VSQRTPDZ256m,\n    VSQRTPDZ256mb,\n    VSQRTPDZ256mbk,\n    VSQRTPDZ256mbkz,\n    VSQRTPDZ256mk,\n    VSQRTPDZ256mkz,\n    VSQRTPDZm,\n    VSQRTPDZmb,\n    VSQRTPDZmbk,\n    VSQRTPDZmbkz,\n    VSQRTPDZmk,\n    VSQRTPDZmkz,\n    VSQRTPHZ128m,\n    VSQRTPHZ128mb,\n    VSQRTPHZ128mbk,\n    VSQRTPHZ128mbkz,\n    VSQRTPHZ128mk,\n    VSQRTPHZ128mkz,\n    VSQRTPHZ256m,\n    VSQRTPHZ256mb,\n    VSQRTPHZ256mbk,\n    VSQRTPHZ256mbkz,\n    VSQRTPHZ256mk,\n    VSQRTPHZ256mkz,\n    VSQRTPHZm,\n    VSQRTPHZmb,\n    VSQRTPHZmbk,\n    VSQRTPHZmbkz,\n    VSQRTPHZmk,\n    VSQRTPHZmkz,\n    VSQRTPSZ128m,\n    VSQRTPSZ128mb,\n    VSQRTPSZ128mbk,\n    VSQRTPSZ128mbkz,\n    VSQRTPSZ128mk,\n    VSQRTPSZ128mkz,\n    VSQRTPSZ256m,\n    VSQRTPSZ256mb,\n    VSQRTPSZ256mbk,\n    VSQRTPSZ256mbkz,\n    VSQRTPSZ256mk,\n    VSQRTPSZ256mkz,\n    VSQRTPSZm,\n    VSQRTPSZmb,\n    VSQRTPSZmbk,\n    VSQRTPSZmbkz,\n    VSQRTPSZmk,\n    VSQRTPSZmkz,\n    VSQRTSDZm_Int,\n    VSQRTSDZm_Intk,\n    VSQRTSDZm_Intkz,\n    VSQRTSHZm_Int,\n    VSQRTSHZm_Intk,\n    VSQRTSHZm_Intkz,\n    VSQRTSSZm_Int,\n    VSQRTSSZm_Intk,\n    VSQRTSSZm_Intkz,\n    VSUBPDZ128rm,\n    VSUBPDZ128rmb,\n    VSUBPDZ128rmbk,\n    VSUBPDZ128rmbkz,\n    VSUBPDZ128rmk,\n    VSUBPDZ128rmkz,\n    VSUBPDZ256rm,\n    VSUBPDZ256rmb,\n    VSUBPDZ256rmbk,\n    VSUBPDZ256rmbkz,\n    VSUBPDZ256rmk,\n    VSUBPDZ256rmkz,\n    VSUBPDZrm,\n    VSUBPDZrmb,\n    VSUBPDZrmbk,\n    VSUBPDZrmbkz,\n    VSUBPDZrmk,\n    VSUBPDZrmkz,\n    VSUBPHZ128rm,\n    VSUBPHZ128rmb,\n    VSUBPHZ128rmbk,\n    VSUBPHZ128rmbkz,\n    VSUBPHZ128rmk,\n    VSUBPHZ128rmkz,\n    VSUBPHZ256rm,\n    VSUBPHZ256rmb,\n    VSUBPHZ256rmbk,\n    VSUBPHZ256rmbkz,\n    VSUBPHZ256rmk,\n    VSUBPHZ256rmkz,\n    VSUBPHZrm,\n    VSUBPHZrmb,\n    VSUBPHZrmbk,\n    VSUBPHZrmbkz,\n    VSUBPHZrmk,\n    VSUBPHZrmkz,\n    VSUBPSZ128rm,\n    VSUBPSZ128rmb,\n    VSUBPSZ128rmbk,\n    VSUBPSZ128rmbkz,\n    VSUBPSZ128rmk,\n    VSUBPSZ128rmkz,\n    VSUBPSZ256rm,\n    VSUBPSZ256rmb,\n    VSUBPSZ256rmbk,\n    VSUBPSZ256rmbkz,\n    VSUBPSZ256rmk,\n    VSUBPSZ256rmkz,\n    VSUBPSZrm,\n    VSUBPSZrmb,\n    VSUBPSZrmbk,\n    VSUBPSZrmbkz,\n    VSUBPSZrmk,\n    VSUBPSZrmkz,\n    VSUBSDZrm_Int,\n    VSUBSDZrm_Intk,\n    VSUBSDZrm_Intkz,\n    VSUBSHZrm_Int,\n    VSUBSHZrm_Intk,\n    VSUBSHZrm_Intkz,\n    VSUBSSZrm_Int,\n    VSUBSSZrm_Intk,\n    VSUBSSZrm_Intkz,\n    VUNPCKHPDZ128rm,\n    VUNPCKHPDZ128rmb,\n    VUNPCKHPDZ128rmbk,\n    VUNPCKHPDZ128rmbkz,\n    VUNPCKHPDZ128rmk,\n    VUNPCKHPDZ128rmkz,\n    VUNPCKHPDZ256rm,\n    VUNPCKHPDZ256rmb,\n    VUNPCKHPDZ256rmbk,\n    VUNPCKHPDZ256rmbkz,\n    VUNPCKHPDZ256rmk,\n    VUNPCKHPDZ256rmkz,\n    VUNPCKHPDZrm,\n    VUNPCKHPDZrmb,\n    VUNPCKHPDZrmbk,\n    VUNPCKHPDZrmbkz,\n    VUNPCKHPDZrmk,\n    VUNPCKHPDZrmkz,\n    VUNPCKHPSZ128rm,\n    VUNPCKHPSZ128rmb,\n    VUNPCKHPSZ128rmbk,\n    VUNPCKHPSZ128rmbkz,\n    VUNPCKHPSZ128rmk,\n    VUNPCKHPSZ128rmkz,\n    VUNPCKHPSZ256rm,\n    VUNPCKHPSZ256rmb,\n    VUNPCKHPSZ256rmbk,\n    VUNPCKHPSZ256rmbkz,\n    VUNPCKHPSZ256rmk,\n    VUNPCKHPSZ256rmkz,\n    VUNPCKHPSZrm,\n    VUNPCKHPSZrmb,\n    VUNPCKHPSZrmbk,\n    VUNPCKHPSZrmbkz,\n    VUNPCKHPSZrmk,\n    VUNPCKHPSZrmkz,\n    VUNPCKLPDZ128rm,\n    VUNPCKLPDZ128rmb,\n    VUNPCKLPDZ128rmbk,\n    VUNPCKLPDZ128rmbkz,\n    VUNPCKLPDZ128rmk,\n    VUNPCKLPDZ128rmkz,\n    VUNPCKLPDZ256rm,\n    VUNPCKLPDZ256rmb,\n    VUNPCKLPDZ256rmbk,\n    VUNPCKLPDZ256rmbkz,\n    VUNPCKLPDZ256rmk,\n    VUNPCKLPDZ256rmkz,\n    VUNPCKLPDZrm,\n    VUNPCKLPDZrmb,\n    VUNPCKLPDZrmbk,\n    VUNPCKLPDZrmbkz,\n    VUNPCKLPDZrmk,\n    VUNPCKLPDZrmkz,\n    VUNPCKLPSZ128rm,\n    VUNPCKLPSZ128rmb,\n    VUNPCKLPSZ128rmbk,\n    VUNPCKLPSZ128rmbkz,\n    VUNPCKLPSZ128rmk,\n    VUNPCKLPSZ128rmkz,\n    VUNPCKLPSZ256rm,\n    VUNPCKLPSZ256rmb,\n    VUNPCKLPSZ256rmbk,\n    VUNPCKLPSZ256rmbkz,\n    VUNPCKLPSZ256rmk,\n    VUNPCKLPSZ256rmkz,\n    VUNPCKLPSZrm,\n    VUNPCKLPSZrmb,\n    VUNPCKLPSZrmbk,\n    VUNPCKLPSZrmbkz,\n    VUNPCKLPSZrmk,\n    VUNPCKLPSZrmkz,\n    VXORPDZ128rm,\n    VXORPDZ128rmb,\n    VXORPDZ128rmbk,\n    VXORPDZ128rmbkz,\n    VXORPDZ128rmk,\n    VXORPDZ128rmkz,\n    VXORPDZ256rm,\n    VXORPDZ256rmb,\n    VXORPDZ256rmbk,\n    VXORPDZ256rmbkz,\n    VXORPDZ256rmk,\n    VXORPDZ256rmkz,\n    VXORPDZrm,\n    VXORPDZrmb,\n    VXORPDZrmbk,\n    VXORPDZrmbkz,\n    VXORPDZrmk,\n    VXORPDZrmkz,\n    VXORPSZ128rm,\n    VXORPSZ128rmb,\n    VXORPSZ128rmbk,\n    VXORPSZ128rmbkz,\n    VXORPSZ128rmk,\n    VXORPSZ128rmkz,\n    VXORPSZ256rm,\n    VXORPSZ256rmb,\n    VXORPSZ256rmbk,\n    VXORPSZ256rmbkz,\n    VXORPSZ256rmk,\n    VXORPSZ256rmkz,\n    VXORPSZrm,\n    VXORPSZrmb,\n    VXORPSZrmbk,\n    VXORPSZrmbkz,\n    VXORPSZrmk,\n    VXORPSZrmkz,\n    // complex & conditionnal memory access (SIB access)\n    TILELOADD,\n    TILELOADDT1,\n    TILELOADDT1_EVEX,\n    TILELOADD_EVEX,\n    TILESTORED,\n    TILESTORED_EVEX,\n    VGATHERDPDYrm,\n    VGATHERDPDrm,\n    VGATHERDPSYrm,\n    VGATHERDPSrm,\n    VGATHERQPDYrm,\n    VGATHERQPDrm,\n    VGATHERQPSYrm,\n    VGATHERQPSrm,\n    VPGATHERDDYrm,\n    VPGATHERDDrm,\n    VPGATHERDQYrm,\n    VPGATHERDQrm,\n    VPGATHERQDYrm,\n    VPGATHERQDrm,\n    VPGATHERQQYrm,\n    VPGATHERQQrm,\n    // farcall\n    FARCALL16m,\n    FARCALL32m,\n    FARCALL64m,\n    FARJMP16m,\n    FARJMP32m,\n    FARJMP64m,\n    // UD1 (trap instruction)\n    UD1Lm,\n    UD1Lr,\n    UD1Qm,\n    UD1Qr,\n    UD1Wm,\n    UD1Wr,\n    // TSXLDTRK\n    XRESLDTRK,\n    XSUSLDTRK,\n    // UINTR\n    CLUI,\n    SENDUIPI,\n    STUI,\n    UIRET,\n    // clang-format on\n};\n\n// instruction that reads memory/stack but without mayLoad\nconst std::set<unsigned> fixupRead{\n    // clang-format off\n    ARPL16mr,\n    BOUNDS16rm,\n    BOUNDS32rm,\n    CMPSB,\n    CMPSL,\n    CMPSQ,\n    CMPSW,\n    LODSB,\n    LODSL,\n    LODSQ,\n    LODSW,\n    LRETI32,\n    LRETI64,\n    LRETI16,\n    LRET32,\n    LRET64,\n    LRET16,\n    MOVDIR64B16,\n    MOVSB,\n    MOVSL,\n    MOVSQ,\n    MOVSW,\n    OR32mi8Locked,\n    RETI32,\n    RETI64,\n    RETI16,\n    RET32,\n    RET64,\n    RET16,\n    SCASB,\n    SCASL,\n    SCASQ,\n    SCASW,\n    // clang-format on\n};\n// instruction that writes memory/stack but without mayStore\nconst std::set<unsigned> fixupWrite{\n    // clang-format off\n    CALL16m,\n    CALL16m_NT,\n    CALL16r,\n    CALL16r_NT,\n    CALL32m,\n    CALL32m_NT,\n    CALL32r,\n    CALL32r_NT,\n    CALL64m,\n    CALL64m_NT,\n    CALL64pcrel32,\n    CALL64r,\n    CALL64r_NT,\n    CALLpcrel16,\n    CALLpcrel32,\n    ENTER,\n    MOVDIR64B16,\n    MOVSB,\n    MOVSL,\n    MOVSQ,\n    MOVSW,\n    OR32mi8Locked,\n    STOSB,\n    STOSL,\n    STOSQ,\n    STOSW,\n    // clang-format on\n};\n// instruction with mayLoad but don't reads memory/stack\nconst std::set<unsigned> fixupNoRead{\n    // clang-format off\n    CLDEMOTE,\n    CLFLUSH,\n    CLFLUSHOPT,\n    CLWB,\n    FEMMS,\n    FXSAVE,\n    FXSAVE64,\n    INT,\n    INT3,\n    LFENCE,\n    LOADIWKEY,\n    MFENCE,\n    MMX_EMMS,\n    MMX_MOVNTQmr,\n    MOVDIRI32,\n    MOVDIRI32_EVEX,\n    MOVDIRI64,\n    MOVDIRI64_EVEX,\n    MWAITrr,\n    PAUSE,\n    PREFETCH,\n    PREFETCHIT0,\n    PREFETCHIT1,\n    PREFETCHNTA,\n    PREFETCHT0,\n    PREFETCHT1,\n    PREFETCHT2,\n    PREFETCHW,\n    PREFETCHWT1,\n    PTWRITE64r,\n    PTWRITEr,\n    RDFSBASE,\n    RDFSBASE64,\n    RDGSBASE,\n    RDGSBASE64,\n    RDPID32,\n    SERIALIZE,\n    SFENCE,\n    STTILECFG,\n    STTILECFG_EVEX,\n    TILERELEASE,\n    TRAP,\n    UMONITOR16,\n    UMONITOR32,\n    UMONITOR64,\n    URDMSRri,\n    URDMSRri_EVEX,\n    URDMSRrr,\n    URDMSRrr_EVEX,\n    UWRMSRir,\n    UWRMSRir_EVEX,\n    UWRMSRrr,\n    UWRMSRrr_EVEX,\n    WRFSBASE,\n    WRFSBASE64,\n    WRGSBASE,\n    WRGSBASE64,\n    XSETBV,\n    // clang-format on\n};\n// instruction with mayStore but don't writes memory/stack\nconst std::set<unsigned> fixupNoWrite{\n    // clang-format off\n    CLDEMOTE,\n    CLFLUSH,\n    CLFLUSHOPT,\n    CLWB,\n    FEMMS,\n    FXRSTOR,\n    FXRSTOR64,\n    INT,\n    INT3,\n    LDMXCSR,\n    LDTILECFG,\n    LDTILECFG_EVEX,\n    LFENCE,\n    LOADIWKEY,\n    MFENCE,\n    MMX_EMMS,\n    MWAITrr,\n    PAUSE,\n    PREFETCH,\n    PREFETCHIT0,\n    PREFETCHIT1,\n    PREFETCHNTA,\n    PREFETCHT0,\n    PREFETCHT1,\n    PREFETCHT2,\n    PREFETCHW,\n    PREFETCHWT1,\n    PTWRITE64m,\n    PTWRITE64r,\n    PTWRITEm,\n    PTWRITEr,\n    RDFSBASE,\n    RDFSBASE64,\n    RDGSBASE,\n    RDGSBASE64,\n    RDPID32,\n    SERIALIZE,\n    SFENCE,\n    TILERELEASE,\n    UMONITOR16,\n    UMONITOR32,\n    UMONITOR64,\n    URDMSRri,\n    URDMSRri_EVEX,\n    URDMSRrr,\n    URDMSRrr_EVEX,\n    UWRMSRir,\n    UWRMSRir_EVEX,\n    UWRMSRrr,\n    UWRMSRrr_EVEX,\n    VLDMXCSR,\n    WRFSBASE,\n    WRFSBASE64,\n    WRGSBASE,\n    WRGSBASE64,\n    XRSTOR,\n    XRSTOR64,\n    XRSTORS,\n    XRSTORS64,\n    XSETBV,\n    // clang-format on\n};\n\n} // namespace\n\nTEST_CASE_METHOD(MemoryAccessTable, \"MemoryAccessTable-CrossCheck\") {\n\n  const QBDI::LLVMCPU &llvmcpu = getCPU(QBDI::CPUMode::DEFAULT);\n  const llvm::MCInstrInfo &MCII = llvmcpu.getMCII();\n\n  for (unsigned opcode = 0; opcode < llvm::X86::INSTRUCTION_LIST_END;\n       opcode++) {\n    if (unsupportedInst.count(opcode) == 1)\n      continue;\n\n    const llvm::MCInstrDesc &desc = MCII.get(opcode);\n    const char *mnemonic = MCII.getName(opcode).data();\n\n    // InstInfo_X86_64.cpp only use inst->getOpcode(). The MCInst doesn't need\n    // to have his operand\n    llvm::MCInst inst;\n    inst.setOpcode(opcode);\n\n    bool doRead = (QBDI::getReadSize(inst, llvmcpu) != 0);\n    bool doWrite = (QBDI::getWriteSize(inst, llvmcpu) != 0);\n    bool mayRead = desc.mayLoad();\n    bool mayWrite = desc.mayStore();\n\n    // the opcode is a pseudo instruction used by LLVM internally\n    if (desc.isPseudo()) {\n      if (doRead or doWrite) {\n        WARN(\"Pseudo instruction \" << mnemonic << \" in InstInfo\");\n      }\n      continue;\n    }\n\n    // some no pseudo instructions are also pseudo ...\n    if ((desc.TSFlags & llvm::X86II::FormMask) == llvm::X86II::Pseudo)\n      continue;\n\n    // not support XOP. (AMD eXtended Operations)\n    if ((desc.TSFlags & llvm::X86II::EncodingMask) == llvm::X86II::XOP)\n      continue;\n\n    bool bypassRead = false;\n    bool bypassWrite = false;\n\n    // llvm mayLoad and mayStore fixup\n    if (fixupRead.count(opcode) == 1) {\n      if (doRead && !mayRead)\n        bypassRead = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupRead\");\n    }\n\n    if (fixupNoRead.count(opcode) == 1) {\n      if (!doRead && mayRead)\n        bypassRead = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupNoRead\");\n    }\n\n    if (fixupWrite.count(opcode) == 1) {\n      if (doWrite && !mayWrite)\n        bypassWrite = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupWrite\");\n    }\n\n    if (fixupNoWrite.count(opcode) == 1) {\n      if (!doWrite && mayWrite)\n        bypassWrite = true;\n      else\n        WARN(\"Unneeded instruction \" << mnemonic << \" in fixupNoWrite\");\n    }\n\n    if (!bypassRead && doRead != mayRead) {\n      if (doRead && !mayRead) {\n        FAIL_CHECK(\"Unexpected read for \" << mnemonic);\n      } else if (!doRead && mayRead) {\n        FAIL_CHECK(\"Missing read for \"\n                   << mnemonic << \" type \"\n                   << (desc.TSFlags & llvm::X86II::FormMask));\n      }\n    }\n\n    if (!bypassWrite && doWrite != mayWrite) {\n      if (doWrite && !mayWrite) {\n        FAIL_CHECK(\"Unexpected write for \" << mnemonic);\n      } else if (!doWrite && mayWrite) {\n        FAIL_CHECK(\"Missing write for \"\n                   << mnemonic << \" type \"\n                   << (desc.TSFlags & llvm::X86II::FormMask));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/Patch/X86_64/Patch_Test_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include \"Patch/Patch_Test.h\"\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-UnalignedCodeForward\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(UnalignedCodeForward_s, inputState, 4096);\n}\n\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-UnalignedCodeBackward\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(UnalignedCodeBackward_s, inputState, 4096);\n}\n\n#ifndef QBDI_PLATFORM_MACOS\nTEST_CASE_METHOD(Patch_Test, \"Patch_Test-LoopCode\") {\n  INFO(\"TEST_SEED=\" << seed_random());\n  QBDI::Context inputState;\n\n  initContext(inputState);\n  comparedExec(LoopCode_s, inputState, 4096);\n}\n#endif\n"
  },
  {
    "path": "test/Patch/X86_64/WIN64_RunRealExec.asm",
    "content": ";\r\n; This file is part of QBDI.\r\n;\r\n; Copyright 2017 - 2025 Quarkslab\r\n;\r\n; Licensed under the Apache License, Version 2.0 (the \"License\");\r\n; you may not use this file except in compliance with the License.\r\n; You may obtain a copy of the License at\r\n;\r\n;     http://www.apache.org/licenses/LICENSE-2.0\r\n;\r\n; Unless required by applicable law or agreed to in writing, software\r\n; distributed under the License is distributed on an \"AS IS\" BASIS,\r\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n; See the License for the specific language governing permissions and\r\n; limitations under the License.\r\n;\r\nPUBLIC runRealExec\r\n\r\n.CODE\r\n\r\nrunRealExec PROC\r\n    push r15;\r\n    push r14;\r\n    push r13;\r\n    push r12;\r\n    push r11;\r\n    push r10;\r\n    push r9;\r\n    push r8;\r\n    push rbp;\r\n    push rdi;\r\n    push rsi;\r\n    push rdx;\r\n    push rcx;\r\n    push rbx;\r\n    push rax;\r\n    mov rdi, rdx;\r\n    call rcx;\r\n    pop rax;\r\n    pop rbx;\r\n    pop rcx;\r\n    pop rdx;\r\n    pop rsi;\r\n    pop rdi;\r\n    pop rbp;\r\n    pop r8;\r\n    pop r9;\r\n    pop r10;\r\n    pop r11;\r\n    pop r12;\r\n    pop r13;\r\n    pop r14;\r\n    pop r15;\r\n    ret;\r\nrunRealExec ENDP\r\n\r\nEND\r\n"
  },
  {
    "path": "test/QBDIBenchmark.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\n#include <QBDI/Logs.h>\n\n#define CATCH_CONFIG_ENABLE_BENCHMARKING\n#include <catch2/catch_session.hpp>\n\nint main(int argc, char **argv) {\n\n  srand(time(nullptr));\n\n  setvbuf(stdout, nullptr, _IONBF, 0);\n  setvbuf(stderr, nullptr, _IONBF, 0);\n\n  return Catch::Session().run(argc, argv);\n  ;\n}\n"
  },
  {
    "path": "test/QBDITest.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\n#include <QBDI/Config.h>\n#include <QBDI/Logs.h>\n\n#include <catch2/catch_session.hpp>\n\n#ifdef QBDI_PLATFORM_WINDOWS\n#include <Windows.h>\n\nstatic int getpid() { return GetCurrentProcessId(); }\n#else\n#include <unistd.h>\n#endif\n\nint main(int argc, char **argv) {\n\n  srand(time(nullptr) | getpid());\n\n  if (getenv(\"TEST_DEBUG\") != nullptr) {\n    QBDI::setLogPriority(QBDI::LogPriority::DEBUG);\n  } else {\n    QBDI::setLogPriority(QBDI::LogPriority::WARNING);\n  }\n  setvbuf(stdout, nullptr, _IONBF, 0);\n  setvbuf(stderr, nullptr, _IONBF, 0);\n\n  return Catch::Session().run(argc, argv);\n  ;\n}\n"
  },
  {
    "path": "test/TestSetup/AARCH64/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InMemoryAssembler_AARCH64.cpp\")\n"
  },
  {
    "path": "test/TestSetup/AARCH64/InMemoryAssembler_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <fstream>\n#include <iostream>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/Platform.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/Object/ELFObjectFile.h\"\n#include \"llvm/Object/ObjectFile.h\"\n#include \"llvm/TargetParser/Triple.h\"\n\nvoid InMemoryObject::perform_reloc(llvm::object::ObjectFile *object,\n                                   const QBDI::LLVMCPUs &llvmcpus) {\n\n  for (auto sit = object->sections().begin(); sit != object->sections().end();\n       ++sit) {\n    auto relocatedSectionExpect = sit->getRelocatedSection();\n    if (!relocatedSectionExpect)\n      continue;\n\n    auto relocatedSection = relocatedSectionExpect.get();\n    if (relocatedSection == object->sections().end())\n      continue;\n    // only relocated the text section, our bytes code\n    if (not relocatedSection->isText()) {\n      QBDI_ABORT(\"Found unexpected relocation sections for {}\",\n                 relocatedSection->getName()->str());\n      continue;\n    }\n\n    // uint8_t* relocatedSectionPtr = static_cast<uint8_t*>(objectBlock.base())\n    // +\n    //   llvm::object::ELFSectionRef(*relocatedSection).getOffset();\n\n    for (auto relocIt = sit->relocation_begin();\n         relocIt != sit->relocation_end(); ++relocIt) {\n      if (relocIt->getOffset() < relocatedSection->getAddress() or\n          relocatedSection->getAddress() + relocatedSection->getSize() <=\n              relocIt->getOffset()) {\n        QBDI_ABORT(\"Symbols not in the target sections\");\n        continue;\n      }\n\n      auto sym = relocIt->getSymbol();\n      if (sym == object->symbol_end()) {\n        QBDI_ABORT(\"Relocation without symbol\");\n      }\n\n      if (!sym->getType() or !sym->getName() or !sym->getAddress() or\n          !sym->getFlags()) {\n        QBDI_ABORT(\"Error when parsing symbol\");\n      }\n\n      QBDI_REQUIRE_ABORT(\n          (*sym->getFlags() &\n           llvm::object::BasicSymbolRef::Flags::SF_Undefined) == 0,\n          \"Relocation to the undefined symbol {}\", sym->getName()->str());\n\n      // int64_t address = *sym->getAddress();\n\n      // if (auto AddendOrErr =\n      // llvm::object::ELFRelocationRef(*relocIt).getAddend())\n      //   address += *AddendOrErr;\n      // else\n      //   llvm::consumeError(AddendOrErr.takeError());\n\n      switch (relocIt->getType()) {\n        default: {\n          llvm::SmallVector<char> relocName;\n          relocIt->getTypeName(relocName);\n          relocName.emplace_back('\\0');\n          QBDI_ABORT(\"Cannot handle relocation type {} to {}\",\n                     relocName.begin(), sym->getName()->str());\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/TestSetup/ARM/CMakeLists.txt",
    "content": "target_sources(QBDITest\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InMemoryAssembler_ARM.cpp\")\n"
  },
  {
    "path": "test/TestSetup/ARM/InMemoryAssembler_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <fstream>\n#include <iostream>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/Platform.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"Target/ARM/ARMSubtarget.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/MC/MCInst.h\"\n#include \"llvm/Object/ELFObjectFile.h\"\n#include \"llvm/Object/ObjectFile.h\"\n#include \"llvm/TargetParser/Triple.h\"\n\nvoid InMemoryObject::perform_reloc(llvm::object::ObjectFile *object,\n                                   const QBDI::LLVMCPUs &llvmcpus) {\n\n  for (auto sit = object->sections().begin(); sit != object->sections().end();\n       ++sit) {\n    auto relocatedSectionExpect = sit->getRelocatedSection();\n    if (!relocatedSectionExpect)\n      continue;\n\n    auto relocatedSection = relocatedSectionExpect.get();\n    if (relocatedSection == object->sections().end())\n      continue;\n    // only relocated the text section, our bytes code\n    if (not relocatedSection->isText()) {\n      QBDI_ABORT(\"Found unexpected relocation sections for {}\",\n                 relocatedSection->getName()->str());\n      continue;\n    }\n\n    uint8_t *relocatedSectionPtr =\n        static_cast<uint8_t *>(objectBlock.base()) +\n        llvm::object::ELFSectionRef(*relocatedSection).getOffset();\n\n    for (auto relocIt = sit->relocation_begin();\n         relocIt != sit->relocation_end(); ++relocIt) {\n      if (relocIt->getOffset() < relocatedSection->getAddress() or\n          relocatedSection->getAddress() + relocatedSection->getSize() <=\n              relocIt->getOffset()) {\n        QBDI_ABORT(\"Symbols not in the target sections\");\n      }\n\n      auto sym = relocIt->getSymbol();\n      if (sym == object->symbol_end()) {\n        QBDI_ABORT(\"Relocation without symbol\");\n      }\n\n      if (!sym->getType() or !sym->getName() or !sym->getAddress() or\n          !sym->getFlags()) {\n        QBDI_ABORT(\"Error when parsing symbol\");\n      }\n\n      QBDI_REQUIRE_ABORT(\n          (*sym->getFlags() &\n           llvm::object::BasicSymbolRef::Flags::SF_Undefined) == 0,\n          \"Relocation to the undefined symbol {}\", sym->getName()->str());\n\n      int64_t address = *sym->getAddress();\n\n      if (auto AddendOrErr =\n              llvm::object::ELFRelocationRef(*relocIt).getAddend())\n        address += *AddendOrErr;\n      else\n        llvm::consumeError(AddendOrErr.takeError());\n\n      switch (relocIt->getType()) {\n        case llvm::ELF::R_ARM_CALL: {\n          QBDI_REQUIRE_ABORT(*sym->getFlags() &\n                                 llvm::object::BasicSymbolRef::Flags::SF_Thumb,\n                             \"The target symbol isn't a thumb method\");\n          const QBDI::LLVMCPU &llvmcpu = llvmcpus.getCPU(QBDI::CPUMode::ARM);\n\n          uint32_t offset =\n              relocIt->getOffset() - relocatedSection->getAddress();\n          QBDI_REQUIRE_ABORT(offset + 4 <= relocatedSection->getSize(),\n                             \"Symbol instruction out of the target section\");\n          QBDI_REQUIRE_ABORT(relocatedSectionPtr == code.data(),\n                             \"Wrong buffer pointer\");\n          uint8_t *instAddr = relocatedSectionPtr + offset;\n\n          llvm::MCInst inst;\n          uint64_t instSize;\n          bool dstatus = llvmcpu.getInstruction(\n              inst, instSize, llvm::ArrayRef<uint8_t>(instAddr, 4),\n              reinterpret_cast<uint64_t>(instAddr));\n          QBDI_REQUIRE_ABORT(dstatus, \"Fail parse the instruction\");\n\n          QBDI_REQUIRE_ABORT(inst.getOpcode() == llvm::ARM::BLXi,\n                             \"Unexpected OPcode\");\n          QBDI_REQUIRE_ABORT(inst.getNumOperands() == 1,\n                             \"Unexpected operand number\");\n          QBDI_REQUIRE_ABORT(inst.getOperand(0).isImm(),\n                             \"Unexpected operand type\");\n\n          inst.getOperand(0).setImm(inst.getOperand(0).getImm() + address -\n                                    relocIt->getOffset());\n\n          llvm::SmallVector<char, 4> stream;\n          llvmcpu.writeInstruction(inst, stream,\n                                   reinterpret_cast<uint64_t>(instAddr));\n          QBDI_REQUIRE_ABORT(stream.size() == 4,\n                             \"Unexpected instruction length\");\n          memcpy(instAddr, stream.data(), 4);\n\n          QBDI_DEBUG(\"Relocated instruction 0x{:x} : 0x{:x}\", offset,\n                     *reinterpret_cast<uint32_t *>(instAddr));\n          break;\n        }\n        case llvm::ELF::R_ARM_THM_CALL: {\n          QBDI_REQUIRE_ABORT((*sym->getFlags() &\n                              llvm::object::BasicSymbolRef::Flags::SF_Thumb) ==\n                                 0,\n                             \"The target symbol isn't an arm method\");\n          const QBDI::LLVMCPU &llvmcpu = llvmcpus.getCPU(QBDI::CPUMode::Thumb);\n\n          uint32_t offset =\n              relocIt->getOffset() - relocatedSection->getAddress();\n          QBDI_REQUIRE_ABORT(offset + 4 <= relocatedSection->getSize(),\n                             \"Symbol instruction out of the target section\");\n          QBDI_REQUIRE_ABORT(relocatedSectionPtr == code.data(),\n                             \"Wrong buffer pointer\");\n          uint8_t *instAddr = relocatedSectionPtr + offset;\n\n          llvm::MCInst inst;\n          uint64_t instSize;\n          bool dstatus = llvmcpu.getInstruction(\n              inst, instSize, llvm::ArrayRef<uint8_t>(instAddr, 4),\n              reinterpret_cast<uint64_t>(instAddr));\n          QBDI_REQUIRE_ABORT(dstatus, \"Fail parse the instruction\");\n\n          QBDI_REQUIRE_ABORT(inst.getOpcode() == llvm::ARM::tBLXi,\n                             \"Unexpected OPcode\");\n          QBDI_REQUIRE_ABORT(inst.getNumOperands() >= 3,\n                             \"Unexpected operand number\");\n          QBDI_REQUIRE_ABORT(inst.getOperand(2).isImm(),\n                             \"Unexpected operand type\");\n\n          if (inst.getNumOperands() == 4 && inst.getOperand(3).isReg() &&\n              inst.getOperand(3).getReg() == llvm::ARM::CPSR &&\n              llvmcpu.getMCII().get(llvm::ARM::tBLXi).getNumOperands() == 3) {\n            inst.erase(inst.begin() + 3);\n          }\n\n          uint32_t value =\n              inst.getOperand(2).getImm() + address - relocIt->getOffset();\n          if (value % 4 != 0) {\n            value += 2;\n          }\n          QBDI_REQUIRE_ABORT((value & 1) == 0, \"Invalid Address 0x{:x}\", value);\n          inst.getOperand(2).setImm(value);\n\n          llvm::SmallVector<char, 4> stream;\n          llvmcpu.writeInstruction(inst, stream,\n                                   reinterpret_cast<uint64_t>(instAddr));\n          QBDI_REQUIRE_ABORT(stream.size() == 4,\n                             \"Unexpected instruction length\");\n          memcpy(instAddr, stream.data(), 4);\n\n          QBDI_DEBUG(\"Relocated instruction 0x{:x} : 0x{:x}\", offset,\n                     *reinterpret_cast<uint32_t *>(instAddr));\n          break;\n        }\n        case llvm::ELF::R_ARM_ABS32: {\n          uint32_t offset =\n              relocIt->getOffset() - relocatedSection->getAddress();\n          QBDI_REQUIRE_ABORT(offset + 4 <= relocatedSection->getSize(),\n                             \"Symbol instruction out of the target section\");\n          QBDI_REQUIRE_ABORT(relocatedSectionPtr == code.data(),\n                             \"Wrong buffer pointer\");\n          uint32_t *instAddr =\n              reinterpret_cast<uint32_t *>(relocatedSectionPtr + offset);\n\n          const bool symbolIsThumb =\n              *sym->getFlags() & llvm::object::BasicSymbolRef::Flags::SF_Thumb;\n\n          int32_t finalAddr =\n              address + reinterpret_cast<QBDI::rword>(code.data());\n          if (symbolIsThumb) {\n            finalAddr |= 1;\n          }\n          *instAddr += finalAddr;\n\n          QBDI_DEBUG(\"Relocated value 0x{:x} : 0x{:x}\", offset, *instAddr);\n          break;\n        }\n        default: {\n          llvm::SmallVector<char> relocName;\n          relocIt->getTypeName(relocName);\n          relocName.emplace_back('\\0');\n          QBDI_ABORT(\"Cannot handle relocation type {} to {}\",\n                     relocName.begin(), sym->getName()->str());\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/TestSetup/CMakeLists.txt",
    "content": "target_sources(\n  QBDITest PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InMemoryAssembler.cpp\"\n                   \"${CMAKE_CURRENT_LIST_DIR}/ShellcodeTester.cpp\")\n\nif(QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "test/TestSetup/InMemoryAssembler.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <fstream>\n#include <iostream>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/Platform.h\"\n#include \"Engine/LLVMCPU.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/MC/MCAsmBackend.h\"\n#include \"llvm/MC/MCAsmInfo.h\"\n#include \"llvm/MC/MCCodeEmitter.h\"\n#include \"llvm/MC/MCContext.h\"\n#include \"llvm/MC/MCInstrInfo.h\"\n#include \"llvm/MC/MCObjectFileInfo.h\"\n#include \"llvm/MC/MCObjectWriter.h\"\n#include \"llvm/MC/MCParser/MCAsmParser.h\"\n#include \"llvm/MC/MCParser/MCTargetAsmParser.h\"\n#include \"llvm/MC/MCRegisterInfo.h\"\n#include \"llvm/MC/MCStreamer.h\"\n#include \"llvm/MC/MCSubtargetInfo.h\"\n#include \"llvm/MC/MCTargetOptions.h\"\n#include \"llvm/MC/TargetRegistry.h\"\n#include \"llvm/Object/ELFObjectFile.h\"\n#include \"llvm/Object/ObjectFile.h\"\n#include \"llvm/Support/SourceMgr.h\"\n#include \"llvm/TargetParser/Host.h\"\n#include \"llvm/TargetParser/SubtargetFeature.h\"\n#include \"llvm/TargetParser/Triple.h\"\n\nvoid writeResult(const char *source,\n                 const llvm::SmallVector<char, 1024> &objectVector) {\n\n  if (getenv(\"DUMP_TEST_ASM\") != nullptr) {\n    std::string filename_source(getenv(\"DUMP_TEST_ASM\"));\n    std::string filename_code(getenv(\"DUMP_TEST_ASM\"));\n\n    filename_source += \".txt\";\n    filename_code += \".bin\";\n\n    std::ofstream source_file(filename_source, std::ios_base::trunc);\n    source_file << source << '\\n';\n    source_file.close();\n\n    std::ofstream code_file(filename_code,\n                            std::ios_base::trunc | std::ios_base::binary);\n    code_file.write(objectVector.data(), objectVector.size());\n    code_file.close();\n  }\n}\n\nInMemoryObject::InMemoryObject(const char *source, const char *cpu,\n                               const char *arch,\n                               const std::vector<std::string> mattrs) {\n  std::unique_ptr<llvm::MCAsmInfo> MAI;\n  std::unique_ptr<llvm::MCContext> MCTX;\n  std::unique_ptr<llvm::MCInstrInfo> MCII;\n  std::unique_ptr<llvm::MCObjectFileInfo> MOFI;\n  std::unique_ptr<llvm::MCRegisterInfo> MRI;\n  std::unique_ptr<llvm::MCSubtargetInfo> MSTI;\n  std::unique_ptr<llvm::MCStreamer> mcStr;\n  const llvm::Target *processTarget;\n  llvm::SourceMgr SrcMgr;\n  std::string tripleName;\n  std::error_code ec;\n  llvm::SmallVector<char, 1024> objectVector;\n\n  llvm::SubtargetFeatures features;\n  for (const auto &e : mattrs) {\n    features.AddFeature(e);\n  }\n  std::string featuresStr = features.getString();\n\n  std::string error;\n  // lookup target\n  llvm::Triple processTriple{llvm::sys::getDefaultTargetTriple()};\n  // make sure we don't accidentally use a weak object file format\n  processTriple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF);\n  processTriple.setOS(llvm::Triple::OSType::Linux);\n  tripleName = llvm::Triple::normalize(processTriple.str());\n  processTarget =\n      llvm::TargetRegistry::lookupTarget(arch, processTriple, error);\n\n  llvm::MCTargetOptions options;\n  // Allocate all LLVM classes\n  MRI = std::unique_ptr<llvm::MCRegisterInfo>(\n      processTarget->createMCRegInfo(tripleName));\n  MAI = std::unique_ptr<llvm::MCAsmInfo>(\n      processTarget->createMCAsmInfo(*MRI, tripleName, options));\n  MCII = std::unique_ptr<llvm::MCInstrInfo>(processTarget->createMCInstrInfo());\n  MSTI = std::unique_ptr<llvm::MCSubtargetInfo>(\n      processTarget->createMCSubtargetInfo(tripleName, cpu, featuresStr));\n  MCTX = std::make_unique<llvm::MCContext>(processTriple, MAI.get(), MRI.get(),\n                                           MSTI.get(), &SrcMgr);\n  MOFI = std::unique_ptr<llvm::MCObjectFileInfo>(\n      processTarget->createMCObjectFileInfo(*MCTX, false));\n  MCTX->setObjectFileInfo(MOFI.get());\n  auto MAB = std::unique_ptr<llvm::MCAsmBackend>(\n      processTarget->createMCAsmBackend(*MSTI, *MRI, options));\n  auto MCE = std::unique_ptr<llvm::MCCodeEmitter>(\n      processTarget->createMCCodeEmitter(*MCII, *MCTX));\n\n  // Wrap output object into raw_ostream\n  // raw_pwrite_string_ostream rpsos(objectStr);\n  llvm::raw_svector_ostream rsos(objectVector);\n  auto objectWriter =\n      std::unique_ptr<llvm::MCObjectWriter>(MAB->createObjectWriter(rsos));\n\n  // Add input to the SourceMgr\n  SrcMgr.AddNewSourceBuffer(\n      llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(source)), llvm::SMLoc());\n  // Set MCStreamer as a MCObjectStreamer\n  mcStr.reset(processTarget->createMCObjectStreamer(\n      MSTI->getTargetTriple(), *MCTX, std::move(MAB), std::move(objectWriter),\n      std::move(MCE), *MSTI));\n  // Create the assembly parsers with --no-deprecated-warn\n  llvm::MCTargetOptions parserOpts;\n  parserOpts.MCNoDeprecatedWarn = true;\n  parserOpts.MCNoWarn = (getenv(\"TEST_WARN\") == nullptr);\n  llvm::MCAsmParser *parser =\n      llvm::createMCAsmParser(SrcMgr, *MCTX, *mcStr, *MAI);\n  llvm::MCTargetAsmParser *tap =\n      processTarget->createMCAsmParser(*MSTI, *parser, *MCII, parserOpts);\n  parser->setTargetParser(*tap);\n  // Finally do something we care about\n  mcStr->initSections(false, *MSTI);\n  REQUIRE_FALSE(parser->Run(true));\n  delete parser;\n  delete tap;\n  // Copy object into new page and make it executable\n  unsigned mFlags = PF::MF_READ | PF::MF_WRITE;\n  if constexpr (QBDI::is_ios)\n    mFlags |= PF::MF_EXEC;\n  objectBlock =\n      QBDI::allocateMappedMemory(objectVector.size(), nullptr, mFlags, ec);\n  memcpy(objectBlock.base(), objectVector.data(), objectVector.size());\n\n  // debug export result\n  writeResult(source, objectVector);\n\n  // LLVM Insanity Oriented Programming\n  auto obj_ptr = llvm::object::ObjectFile::createObjectFile(\n      llvm::MemoryBufferRef(llvm::StringRef((const char *)objectBlock.base(),\n                                            objectBlock.allocatedSize()),\n                            \"\"));\n  if (not obj_ptr) {\n    QBDI_ABORT(\"Failed to load input file\");\n  }\n\n  // Finding the .text section of the object\n  llvm::object::ObjectFile *object = obj_ptr.get().get();\n  llvm::StringRef text_section_content;\n\n  for (auto sit = object->sections().begin(); sit != object->sections().end();\n       ++sit) {\n    // llvm::errs() << \"sections name : \" << sit->getName().get() << '\\n';\n    if (sit->isText()) {\n      if (auto E = sit->getContents()) {\n        // verify that only one Text section\n        REQUIRE(text_section_content.empty());\n        text_section_content = *E;\n        REQUIRE_FALSE(text_section_content.empty());\n      } else {\n        llvm::handleAllErrors(\n            E.takeError(), [](const llvm::ErrorInfoBase &EIB) {\n              QBDI_ABORT(\"Failed to load text section: {}\", EIB.message());\n            });\n        return;\n      }\n    }\n  }\n  REQUIRE_FALSE(text_section_content.empty());\n  code = llvm::ArrayRef<uint8_t>((const uint8_t *)text_section_content.data(),\n                                 text_section_content.size());\n\n  // perform relocation if needed\n  QBDI::LLVMCPUs llvmcpus(cpu, mattrs);\n  perform_reloc(object, llvmcpus);\n\n  // Finally, set the page executable\n  if constexpr (not QBDI::is_ios)\n    llvm::sys::Memory::protectMappedMemory(objectBlock,\n                                           PF::MF_READ | PF::MF_EXEC);\n}\n\nInMemoryObject::~InMemoryObject() { QBDI::releaseMappedMemory(objectBlock); }\n"
  },
  {
    "path": "test/TestSetup/InMemoryAssembler.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INMEMORYASSEMBLER_H\n#define INMEMORYASSEMBLER_H\n\n#include <string>\n#include <vector>\n\n#include \"llvm/ADT/ArrayRef.h\"\n#include \"llvm/Support/Memory.h\"\n\nnamespace llvm::object {\nclass ObjectFile;\n}\n\nnamespace QBDI {\nclass LLVMCPUs;\n}\n\nclass InMemoryObject {\nprivate:\n  void perform_reloc(llvm::object::ObjectFile *obj,\n                     const QBDI::LLVMCPUs &llvmcpus);\n\nprotected:\n  using PF = llvm::sys::Memory::ProtectionFlags;\n\n  llvm::sys::MemoryBlock objectBlock;\n  llvm::ArrayRef<uint8_t> code;\n\npublic:\n  InMemoryObject(const char *source, const char *cpu = \"\",\n                 const char *arch = \"\",\n                 const std::vector<std::string> mattrs = {});\n\n  InMemoryObject(const InMemoryObject &vm) = delete;\n  InMemoryObject(InMemoryObject &&vm) = default;\n  InMemoryObject &operator=(const InMemoryObject &vm) = delete;\n  InMemoryObject &operator=(const InMemoryObject &&vm) = delete;\n\n  ~InMemoryObject();\n\n  llvm::ArrayRef<uint8_t> getCode() { return code; }\n};\n\n#endif\n"
  },
  {
    "path": "test/TestSetup/LLVMTestEnv.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LLVMTESTENV_H\n#define LLVMTESTENV_H\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"Engine/LLVMCPU.h\"\n\nclass LLVMTestEnv : public QBDI::LLVMCPUs {};\n\n#endif\n"
  },
  {
    "path": "test/TestSetup/ShellcodeTester.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"TestSetup/ShellcodeTester.h\"\n#include <catch2/catch_test_macros.hpp>\n#include \"Utility/LogSys.h\"\n\nllvm::sys::MemoryBlock ShellcodeTester::allocateStack(QBDI::rword size) {\n  std::error_code ec;\n  llvm::sys::MemoryBlock stackBlock;\n\n  stackBlock = llvm::sys::Memory::allocateMappedMemory(\n      size, nullptr, PF::MF_READ | PF::MF_WRITE, ec);\n  CHECK(0 == ec.value());\n  memset(stackBlock.base(), 0, stackBlock.allocatedSize());\n  return stackBlock;\n}\n\nvoid ShellcodeTester::freeStack(llvm::sys::MemoryBlock &memoryBlock) {\n  CHECK(0 == llvm::sys::Memory::releaseMappedMemory(memoryBlock).value());\n}\n\nvoid ShellcodeTester::comparedExec(const char *source, QBDI::Context &inputCtx,\n                                   QBDI::rword stackSize) {\n  InMemoryObject object = compileWithContextSwitch(source);\n\n  QBDI::Context realCtx, jitCtx;\n  llvm::sys::MemoryBlock realStack = allocateStack(stackSize);\n  llvm::sys::MemoryBlock jitStack = allocateStack(stackSize);\n  REQUIRE(realStack.allocatedSize() == jitStack.allocatedSize());\n\n  const llvm::ArrayRef<uint8_t> &code = object.getCode();\n  llvm::sys::Memory::InvalidateInstructionCache(code.data(), code.size());\n\n  // use the same stack for the 2 executions\n  memset(jitStack.base(), 0, jitStack.allocatedSize());\n  realCtx = realExec(code, inputCtx, jitStack);\n  memcpy(realStack.base(), jitStack.base(), jitStack.allocatedSize());\n\n  memset(jitStack.base(), 0, jitStack.allocatedSize());\n  jitCtx = jitExec(code, inputCtx, jitStack);\n\n  for (uint32_t i = 0; i < QBDI::AVAILABLE_GPR; i++) {\n    INFO(\"The offset is \" << i);\n    CHECK(QBDI_GPR_GET(&realCtx.gprState, i) ==\n          QBDI_GPR_GET(&jitCtx.gprState, i));\n  }\n\n#if defined(QBDI_ARCH_AARCH64)\n  for (uint32_t i = 0; i < QBDI_NUM_FPR; i++) {\n    INFO(\"The offset is \" << i);\n    CHECK(((__uint128_t *)&realCtx.fprState)[i] ==\n          ((__uint128_t *)&jitCtx.fprState)[i]);\n  }\n#elif !defined(_QBDI_ASAN_ENABLED_) || !defined(QBDI_ARCH_X86_64)\n  for (uint32_t i = 0; i < sizeof(QBDI::FPRState); i++) {\n    INFO(\"The offset is \" << i);\n    CHECK(((char *)&realCtx.fprState)[i] == ((char *)&jitCtx.fprState)[i]);\n  }\n#endif\n  for (uint32_t i = 0; i < realStack.allocatedSize() - sizeof(QBDI::rword);\n       i++) {\n    INFO(\"The offset is \" << i << \" / \" << realStack.allocatedSize());\n    CHECK(((char *)realStack.base())[i] == ((char *)jitStack.base())[i]);\n  }\n\n  inputCtx = jitCtx;\n\n  freeStack(realStack);\n  freeStack(jitStack);\n}\n"
  },
  {
    "path": "test/TestSetup/ShellcodeTester.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef SHELLCODETESTER_H\n#define SHELLCODETESTER_H\n\n#include <stdlib.h>\n#include <string.h>\n\n#include \"QBDI/VM.h\"\n#include \"ExecBlock/Context.h\"\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\nclass ShellcodeTester {\nprotected:\n  using PF = llvm::sys::Memory::ProtectionFlags;\n\n  QBDI::VM vm;\n\npublic:\n  ShellcodeTester(const std::string &cpu = \"\",\n                  const std::vector<std::string> &mattrs = {})\n      : vm(cpu, mattrs) {}\n\n  virtual llvm::sys::MemoryBlock allocateStack(QBDI::rword size);\n\n  virtual void freeStack(llvm::sys::MemoryBlock &memoryBlock);\n\n  virtual InMemoryObject compileWithContextSwitch(const char *source) = 0;\n\n  virtual QBDI::Context jitExec(llvm::ArrayRef<uint8_t> code,\n                                QBDI::Context &inputCtx,\n                                llvm::sys::MemoryBlock &stack) = 0;\n\n  virtual QBDI::Context realExec(llvm::ArrayRef<uint8_t> code,\n                                 QBDI::Context &inputCtx,\n                                 llvm::sys::MemoryBlock &stack) = 0;\n\n  virtual void comparedExec(const char *source, QBDI::Context &inputCtx,\n                            QBDI::rword stackSize);\n};\n\n#endif\n"
  },
  {
    "path": "test/TestSetup/X86/CMakeLists.txt",
    "content": "target_sources(QBDITest\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InMemoryAssembler_X86.cpp\")\n"
  },
  {
    "path": "test/TestSetup/X86/InMemoryAssembler_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <fstream>\n#include <iostream>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/Platform.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/Object/ELFObjectFile.h\"\n#include \"llvm/Object/ObjectFile.h\"\n#include \"llvm/TargetParser/Triple.h\"\n\nvoid InMemoryObject::perform_reloc(llvm::object::ObjectFile *object,\n                                   const QBDI::LLVMCPUs &llvmcpus) {\n\n  for (auto sit = object->sections().begin(); sit != object->sections().end();\n       ++sit) {\n    auto relocatedSectionExpect = sit->getRelocatedSection();\n    if (!relocatedSectionExpect)\n      continue;\n\n    auto relocatedSection = relocatedSectionExpect.get();\n    if (relocatedSection == object->sections().end())\n      continue;\n    // only relocated the text section, our bytes code\n    if (not relocatedSection->isText()) {\n      QBDI_ABORT(\"Found unexpected relocation sections for {}\",\n                 relocatedSection->getName()->str());\n      continue;\n    }\n\n    // uint8_t* relocatedSectionPtr = static_cast<uint8_t*>(objectBlock.base())\n    // +\n    //   llvm::object::ELFSectionRef(*relocatedSection).getOffset();\n\n    for (auto relocIt = sit->relocation_begin();\n         relocIt != sit->relocation_end(); ++relocIt) {\n      if (relocIt->getOffset() < relocatedSection->getAddress() or\n          relocatedSection->getAddress() + relocatedSection->getSize() <=\n              relocIt->getOffset()) {\n        QBDI_ABORT(\"Symbols not in the target sections\");\n        continue;\n      }\n\n      auto sym = relocIt->getSymbol();\n      if (sym == object->symbol_end()) {\n        QBDI_ABORT(\"Relocation without symbol\");\n      }\n\n      if (!sym->getType() or !sym->getName() or !sym->getAddress() or\n          !sym->getFlags()) {\n        QBDI_ABORT(\"Error when parsing symbol\");\n      }\n\n      QBDI_REQUIRE_ABORT(\n          (*sym->getFlags() &\n           llvm::object::BasicSymbolRef::Flags::SF_Undefined) == 0,\n          \"Relocation to the undefined symbol {}\", sym->getName()->str());\n\n      // int64_t address = *sym->getAddress();\n\n      // if (auto AddendOrErr =\n      // llvm::object::ELFRelocationRef(*relocIt).getAddend())\n      //   address += *AddendOrErr;\n      // else\n      //   llvm::consumeError(AddendOrErr.takeError());\n\n      switch (relocIt->getType()) {\n        default: {\n          llvm::SmallVector<char> relocName;\n          relocIt->getTypeName(relocName);\n          relocName.emplace_back('\\0');\n          QBDI_ABORT(\"Cannot handle relocation type {} to {}\",\n                     relocName.begin(), sym->getName()->str());\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/TestSetup/X86_64/CMakeLists.txt",
    "content": "target_sources(QBDITest\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/InMemoryAssembler_X86_64.cpp\")\n"
  },
  {
    "path": "test/TestSetup/X86_64/InMemoryAssembler_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <catch2/catch_test_macros.hpp>\n#include <fstream>\n#include <iostream>\n\n#include \"TestSetup/InMemoryAssembler.h\"\n\n#include \"QBDI/Platform.h\"\n#include \"Utility/LogSys.h\"\n#include \"Utility/System.h\"\n\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/ADT/Twine.h\"\n#include \"llvm/Object/ELFObjectFile.h\"\n#include \"llvm/Object/ObjectFile.h\"\n#include \"llvm/TargetParser/Triple.h\"\n\nvoid InMemoryObject::perform_reloc(llvm::object::ObjectFile *object,\n                                   const QBDI::LLVMCPUs &llvmcpus) {\n\n  for (auto sit = object->sections().begin(); sit != object->sections().end();\n       ++sit) {\n    auto relocatedSectionExpect = sit->getRelocatedSection();\n    if (!relocatedSectionExpect)\n      continue;\n\n    auto relocatedSection = relocatedSectionExpect.get();\n    if (relocatedSection == object->sections().end())\n      continue;\n    // only relocated the text section, our bytes code\n    if (not relocatedSection->isText()) {\n      QBDI_ABORT(\"Found unexpected relocation sections for {}\",\n                 relocatedSection->getName()->str());\n      continue;\n    }\n\n    // uint8_t* relocatedSectionPtr = static_cast<uint8_t*>(objectBlock.base())\n    // +\n    //   llvm::object::ELFSectionRef(*relocatedSection).getOffset();\n\n    for (auto relocIt = sit->relocation_begin();\n         relocIt != sit->relocation_end(); ++relocIt) {\n      if (relocIt->getOffset() < relocatedSection->getAddress() or\n          relocatedSection->getAddress() + relocatedSection->getSize() <=\n              relocIt->getOffset()) {\n        QBDI_ABORT(\"Symbols not in the target sections\");\n        continue;\n      }\n\n      auto sym = relocIt->getSymbol();\n      if (sym == object->symbol_end()) {\n        QBDI_ABORT(\"Relocation without symbol\");\n      }\n\n      if (!sym->getType() or !sym->getName() or !sym->getAddress() or\n          !sym->getFlags()) {\n        QBDI_ABORT(\"Error when parsing symbol\");\n      }\n\n      QBDI_REQUIRE_ABORT(\n          (*sym->getFlags() &\n           llvm::object::BasicSymbolRef::Flags::SF_Undefined) == 0,\n          \"Relocation to the undefined symbol {}\", sym->getName()->str());\n\n      // int64_t address = *sym->getAddress();\n\n      // if (auto AddendOrErr =\n      // llvm::object::ELFRelocationRef(*relocIt).getAddend())\n      //   address += *AddendOrErr;\n      // else\n      //   llvm::consumeError(AddendOrErr.takeError());\n\n      switch (relocIt->getType()) {\n        default: {\n          llvm::SmallVector<char> relocName;\n          relocIt->getTypeName(relocName);\n          relocName.emplace_back('\\0');\n          QBDI_ABORT(\"Cannot handle relocation type {} to {}\",\n                     relocName.begin(), sym->getName()->str());\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tools/CMakeLists.txt",
    "content": "if(QBDI_TOOLS_QBDIPRELOAD)\n  # Add QBDI preload library\n  add_subdirectory(QBDIPreload)\n\n  if(QBDI_TOOLS_VALIDATOR)\n    # Add validator\n    add_subdirectory(validator)\n  endif()\n\n  if(QBDI_PLATFORM_WINDOWS)\n    add_subdirectory(QBDIWinPreloader)\n  endif()\n\nendif()\n\nif(QBDI_TOOLS_PYQBDI)\n  message(STATUS \"Compile PyQBDI\")\n  # Add pyqbdi\n  add_subdirectory(pyqbdi)\nendif()\n"
  },
  {
    "path": "tools/QBDIPreload/CMakeLists.txt",
    "content": "add_library(QBDIPreload STATIC)\nadd_library(QBDIPreload::QBDIPreload ALIAS QBDIPreload)\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/src/CMakeLists.txt\")\ntarget_compile_options(QBDIPreload PRIVATE $<$<COMPILE_LANGUAGE:C>:\n                                           ${QBDI_COMMON_C_FLAGS}>)\ntarget_compile_definitions(QBDIPreload PRIVATE ${QBDI_COMMON_DEFINITION})\n\n# Also add build directory as include path for the mach_exc.h header\ntarget_include_directories(\n  QBDIPreload\n  PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../../include>\n          $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../../include-static>\n          $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../include>\n          $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>\n          $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n          $<INSTALL_INTERFACE:include>)\n\ntarget_include_directories(\n  QBDIPreload INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)\n\ninstall(\n  TARGETS QBDIPreload\n  EXPORT QBDIPreload_targets\n  ARCHIVE DESTINATION lib)\n\ninstall(FILES include/QBDIPreload.h DESTINATION include/)\n\ninstall(\n  EXPORT QBDIPreload_targets\n  FILE QBDIPreloadConfig.cmake\n  NAMESPACE QBDIPreload::\n  DESTINATION ${PRELOAD_RESOURCES_PREFIX}/cmake)\n\ninstall(\n  EXPORT QBDIPreload_targets\n  FILE QBDIPreload${QBDI_ARCH}Config.cmake\n  NAMESPACE QBDIPreload::${QBDI_ARCH}::\n  DESTINATION ${PRELOAD_RESOURCES_PREFIX}/cmake)\n"
  },
  {
    "path": "tools/QBDIPreload/include/QBDIPreload.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_H\n#define QBDIPRELOAD_H\n\n#include \"QBDI.h\"\n\n#if defined(_WIN32) || defined(WIN32)\n#include <Windows.h>\n#endif\n\n#ifdef __cplusplus\nnamespace QBDI {\nextern \"C\" {\n#endif\n\n/** @brief No error */\n#define QBDIPRELOAD_NO_ERROR 0\n/** @brief Startup step not handled by callback */\n#define QBDIPRELOAD_NOT_HANDLED 1\n/** @brief Error in the startup (preload) process */\n#define QBDIPRELOAD_ERR_STARTUP_FAILED 2\n\n/**\n * A C pre-processor macro declaring a constructor.\n *\n * @warning `QBDIPRELOAD_INIT` must be used once in any project using\n * QBDIPreload. It declares a constructor, so it must be placed like a function\n * declaration on a single line.\n */\n#ifdef __cplusplus\n\n#if defined(_WIN32) || defined(WIN32)\n#define QBDIPRELOAD_INIT                                      \\\n  BOOLEAN WINAPI DllMain(HINSTANCE hDllHandle, DWORD nReason, \\\n                         LPVOID Reserved) {                   \\\n    UNREFERENCED_PARAMETER(hDllHandle);                       \\\n    UNREFERENCED_PARAMETER(Reserved);                         \\\n    QBDI::qbdipreload_attach_init();                          \\\n    return QBDI::qbdipreload_hook_init(nReason);              \\\n  }\n#else // not WIN32\n#define QBDIPRELOAD_INIT \\\n  __attribute__((constructor)) void init() { QBDI::qbdipreload_hook_init(); }\n#endif\n\n#else // not __cplusplus\n\n#if defined(_WIN32) || defined(WIN32)\n#define QBDIPRELOAD_INIT                                      \\\n  BOOLEAN WINAPI DllMain(HINSTANCE hDllHandle, DWORD nReason, \\\n                         LPVOID Reserved) {                   \\\n    UNREFERENCED_PARAMETER(hDllHandle);                       \\\n    UNREFERENCED_PARAMETER(Reserved);                         \\\n    qbdipreload_attach_init();                                \\\n    return qbdipreload_hook_init(nReason);                    \\\n  }\n#else // not WIN32\n#define QBDIPRELOAD_INIT \\\n  __attribute__((constructor)) void init() { qbdipreload_hook_init(); }\n#endif\n\n#endif\n\n/*\n * Public APIs\n */\n\n/** Convert a QBDIPreload GPR context (platform dependent) to a QBDI GPR state.\n * @param[in] gprCtx     Platform GPRState pointer\n * @param[in] gprState   QBDI GPRState pointer\n */\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\n\n/** Convert a QBDIPreload FPR context (platform dependent) to a QBDI FPR state.\n * @param[in] fprCtx     Platform FPRState pointer\n * @param[in] fprState   QBDI FPRState pointer\n */\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\n/** Enable QBDIPreload hook on the main function (using its address)\n *\n * @warning It MUST be used in `qbdipreload_on_start` if you want to handle this\n * step. The assumed `main` address is provided as a callback argument.\n *\n * @param[in] main       Pointer to the main function\n */\nint qbdipreload_hook_main(void *main);\n\n/*\n * QBDIPreload callbacks\n *\n * The following functions are callbacks that MUST be defined\n * in any project based on libboostrap.\n *\n * All of them are a unique step in the preload process.\n * If no custom actions are required in a given step,\n * QBDIPRELOAD_NOT_HANDLED can simply be returned and\n * default behavior will be used.\n */\n\n/*! Function called when preload is on a program entry point\n * (interposed start or an early constructor).\n * It provides the main function address, that can be used\n * to place a hook using the `qbdipreload_hook_main` API.\n * @param[in]   main    Address of the main function\n * @return      int     QBDIPreload state\n */\nextern int qbdipreload_on_start(void *main);\n\n/*! Function called when preload hook on main function is triggered.\n * It provides original (and platforms dependent) GPR and FPR contexts.\n * They can be converted to QBDI states, using `qbdipreload_threadCtxToGPRState`\n * and `qbdipreload_floatCtxToFPRState` APIs.\n * @param[in]   gprCtx  Original GPR context\n * @param[in]   fpuCtx  Original FPU context\n * @return      int     QBDIPreload state\n */\nextern int qbdipreload_on_premain(void *gprCtx, void *fpuCtx);\n\n/*! Function called when preload has successfully hijacked\n * the main thread and we are in place of the original main\n * function (with the same thread state).\n * @param[in]   argc    Original argc\n * @param[in]   argv    Original argv\n * @return      int     QBDIPreload state\n */\nextern int qbdipreload_on_main(int argc, char **argv);\n\n/*! Function called when preload is done and we have a valid\n * QBDI VM object on which we can call run (after some last initializations).\n * @param[in]  vm          VM instance.\n * @param[in]  start       Start address of the range (included).\n * @param[in]  stop        End address of the range (excluded).\n * @return     int         QBDIPreload state\n */\nextern int qbdipreload_on_run(VMInstanceRef vm, rword start, rword stop);\n\n/*! Function called when process is exiting (using `_exit` or `exit`).\n * @param[in]  status  exit status\n * @return     int     QBDIPreload state\n */\nextern int qbdipreload_on_exit(int status);\n\n/*\n * Private API\n */\n\n#if defined(_WIN32) || defined(WIN32)\n\nextern BOOL g_bIsAttachMode; /* TRUE if attach mode is activated */\n\nQBDI_EXPORT BOOLEAN qbdipreload_hook_init(DWORD nReason);\nBOOLEAN qbdipreload_attach_init();\nvoid qbdipreload_attach_close();\nQBDI_EXPORT BOOLEAN qbdipreload_read_shmem(LPVOID lpData, DWORD dwMaxBytesRead);\n\n#else\n\nint qbdipreload_hook_init();\n\n#endif\n\nvoid *qbdipreload_setup_exception_handler(uint32_t target, uint32_t mask,\n                                          void *handler);\n\n#ifdef __cplusplus\n} // \"C\"\n}\n#endif\n\n#endif // QBDIPRELOAD_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/AARCH64/CMakeLists.txt",
    "content": "if(QBDI_PLATFORM_LINUX)\n  target_sources(QBDIPreload\n                 PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_AARCH64.c\")\nelseif(QBDI_PLATFORM_MACOS)\n  target_sources(QBDIPreload\n                 PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/macos_AARCH64.c\")\nelse()\n  message(FATAL_ERROR \"No QBDIPreload for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "tools/QBDIPreload/src/AARCH64/linux_AARCH64.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdlib.h>\n\n#include \"linux_AARCH64.h\"\n\n// see arch/arm64/include/uapi/asm/sigcontext.h\ntypedef struct {\n  struct {\n    uint32_t magic;\n    uint32_t size;\n  } head;\n  uint32_t fpsr;\n  uint32_t fpcr;\n  __uint128_t vregs[32];\n} fpsimd_context;\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  const ucontext_t *uap = (const ucontext_t *)gprCtx;\n\n  gprState->x0 = uap->uc_mcontext.regs[0];\n  gprState->x1 = uap->uc_mcontext.regs[1];\n  gprState->x2 = uap->uc_mcontext.regs[2];\n  gprState->x3 = uap->uc_mcontext.regs[3];\n  gprState->x4 = uap->uc_mcontext.regs[4];\n  gprState->x5 = uap->uc_mcontext.regs[5];\n  gprState->x6 = uap->uc_mcontext.regs[6];\n  gprState->x7 = uap->uc_mcontext.regs[7];\n  gprState->x8 = uap->uc_mcontext.regs[8];\n  gprState->x9 = uap->uc_mcontext.regs[9];\n  gprState->x10 = uap->uc_mcontext.regs[10];\n  gprState->x11 = uap->uc_mcontext.regs[11];\n  gprState->x12 = uap->uc_mcontext.regs[12];\n  gprState->x13 = uap->uc_mcontext.regs[13];\n  gprState->x14 = uap->uc_mcontext.regs[14];\n  gprState->x15 = uap->uc_mcontext.regs[15];\n  gprState->x16 = uap->uc_mcontext.regs[16];\n  gprState->x17 = uap->uc_mcontext.regs[17];\n  gprState->x18 = uap->uc_mcontext.regs[18];\n  gprState->x19 = uap->uc_mcontext.regs[19];\n  gprState->x20 = uap->uc_mcontext.regs[20];\n  gprState->x21 = uap->uc_mcontext.regs[21];\n  gprState->x22 = uap->uc_mcontext.regs[22];\n  gprState->x23 = uap->uc_mcontext.regs[23];\n  gprState->x24 = uap->uc_mcontext.regs[24];\n  gprState->x25 = uap->uc_mcontext.regs[25];\n  gprState->x26 = uap->uc_mcontext.regs[26];\n  gprState->x27 = uap->uc_mcontext.regs[27];\n  gprState->x28 = uap->uc_mcontext.regs[28];\n  gprState->x29 = uap->uc_mcontext.regs[29];\n  gprState->lr = uap->uc_mcontext.regs[30];\n\n  gprState->sp = uap->uc_mcontext.sp;\n  gprState->pc = uap->uc_mcontext.pc;\n  gprState->nzcv = uap->uc_mcontext.pstate & 0xf0000000;\n}\n\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState) {\n  const ucontext_t *uap = (const ucontext_t *)fprCtx;\n  const fpsimd_context *fuap =\n      (const fpsimd_context *)&(uap->uc_mcontext.__reserved);\n\n  fprState->v0 = fuap->vregs[0];\n  fprState->v1 = fuap->vregs[1];\n  fprState->v2 = fuap->vregs[2];\n  fprState->v3 = fuap->vregs[3];\n  fprState->v4 = fuap->vregs[4];\n  fprState->v5 = fuap->vregs[5];\n  fprState->v6 = fuap->vregs[6];\n  fprState->v7 = fuap->vregs[7];\n  fprState->v8 = fuap->vregs[8];\n  fprState->v9 = fuap->vregs[9];\n  fprState->v10 = fuap->vregs[10];\n  fprState->v11 = fuap->vregs[11];\n  fprState->v12 = fuap->vregs[12];\n  fprState->v13 = fuap->vregs[13];\n  fprState->v14 = fuap->vregs[14];\n  fprState->v15 = fuap->vregs[15];\n  fprState->v16 = fuap->vregs[16];\n  fprState->v17 = fuap->vregs[17];\n  fprState->v18 = fuap->vregs[18];\n  fprState->v19 = fuap->vregs[19];\n  fprState->v20 = fuap->vregs[20];\n  fprState->v21 = fuap->vregs[21];\n  fprState->v22 = fuap->vregs[22];\n  fprState->v23 = fuap->vregs[23];\n  fprState->v24 = fuap->vregs[24];\n  fprState->v25 = fuap->vregs[25];\n  fprState->v26 = fuap->vregs[26];\n  fprState->v27 = fuap->vregs[27];\n  fprState->v28 = fuap->vregs[28];\n  fprState->v29 = fuap->vregs[29];\n  fprState->v30 = fuap->vregs[30];\n  fprState->v31 = fuap->vregs[31];\n  fprState->fpcr = fuap->fpcr;\n  fprState->fpsr = fuap->fpsr;\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap) {\n  uap->uc_mcontext.sp = (rword)newStack + sizeStack - 16;\n  uap->uc_mcontext.regs[29] = (rword)newStack + sizeStack - 16;\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size) {\n  size_t i;\n  for (i = 0; i < size; i++) {\n    if ((modules[i].permission & QBDI_PF_EXEC) &&\n        (strstr(modules[i].name, \"libc-2.\") ||\n         strstr(modules[i].name, \"libc.so.\") ||\n         strstr(modules[i].name, \"ld-2.\") ||\n         strstr(modules[i].name, \"ld-linux-\") ||\n         strstr(modules[i].name, \"libpthread-\") ||\n         strstr(modules[i].name, \"libcofi\") || modules[i].name[0] == 0)) {\n      qbdi_removeInstrumentedRange(vm, modules[i].start, modules[i].end);\n    }\n  }\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/AARCH64/linux_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_AARCH64_H\n#define QBDIPRELOAD_AARCH64_H\n\n#include <dlfcn.h>\n#include <signal.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\n#define SIGBRK SIGTRAP\n\nstatic inline void *correctAddress(void *address, long *bytecode, long *mask) {\n  *bytecode = 0xD4207FE0; // brk 3ff;\n  *mask = 0xFFFFFFFF;\n  return address;\n}\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\nstatic inline rword getReturnAddress(GPRState *gprState) {\n  return QBDI_GPR_GET(gprState, REG_LR);\n}\n\nstatic inline void fix_ucontext_t(ucontext_t *uap) {\n  // nothing to do\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap);\n\nstatic inline void setPC(ucontext_t *uap, rword address) {\n  uap->uc_mcontext.pc = address;\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size);\n\n#endif // QBDIPRELOAD_AARCH64_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/AARCH64/macos_AARCH64.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"macos_AARCH64.h\"\n#include \"QBDIPreload.h\"\n\n#include <mach/thread_act.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  const arm_thread_state64_t *ts = (arm_thread_state64_t *)gprCtx;\n\n  gprState->x0 = ts->__x[0];\n  gprState->x1 = ts->__x[1];\n  gprState->x2 = ts->__x[2];\n  gprState->x3 = ts->__x[3];\n  gprState->x4 = ts->__x[4];\n  gprState->x5 = ts->__x[5];\n  gprState->x6 = ts->__x[6];\n  gprState->x7 = ts->__x[7];\n  gprState->x8 = ts->__x[8];\n  gprState->x9 = ts->__x[9];\n  gprState->x10 = ts->__x[10];\n  gprState->x11 = ts->__x[11];\n  gprState->x12 = ts->__x[12];\n  gprState->x13 = ts->__x[13];\n  gprState->x14 = ts->__x[14];\n  gprState->x15 = ts->__x[15];\n  gprState->x16 = ts->__x[16];\n  gprState->x17 = ts->__x[17];\n  gprState->x18 = ts->__x[18];\n  gprState->x19 = ts->__x[19];\n  gprState->x20 = ts->__x[20];\n  gprState->x21 = ts->__x[21];\n  gprState->x22 = ts->__x[22];\n  gprState->x23 = ts->__x[23];\n  gprState->x24 = ts->__x[24];\n  gprState->x25 = ts->__x[25];\n  gprState->x26 = ts->__x[26];\n  gprState->x27 = ts->__x[27];\n  gprState->x28 = ts->__x[28];\n  gprState->x29 = __darwin_arm_thread_state64_get_fp(*ts);\n  gprState->lr = __darwin_arm_thread_state64_get_lr(*ts);\n  gprState->sp = __darwin_arm_thread_state64_get_sp(*ts);\n  gprState->nzcv = ts->__cpsr;\n  gprState->pc = __darwin_arm_thread_state64_get_pc(*ts);\n}\n\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState) {\n  const arm_neon_state64_t *fs = (arm_neon_state64_t *)fprCtx;\n\n#if __DARWIN_UNIX03\n#define V_POS __v\n#define FPSR_POS __fpsr\n#define FPCR_POS __fpcr\n#else\n#define V_POS q\n#define FPSR_POS fpsr\n#define FPCR_POS fpcr\n#endif\n\n  fprState->v0 = fs->V_POS[0];\n  fprState->v1 = fs->V_POS[1];\n  fprState->v2 = fs->V_POS[2];\n  fprState->v3 = fs->V_POS[3];\n  fprState->v4 = fs->V_POS[4];\n  fprState->v5 = fs->V_POS[5];\n  fprState->v6 = fs->V_POS[6];\n  fprState->v7 = fs->V_POS[7];\n  fprState->v8 = fs->V_POS[8];\n  fprState->v9 = fs->V_POS[9];\n  fprState->v10 = fs->V_POS[10];\n  fprState->v11 = fs->V_POS[11];\n  fprState->v12 = fs->V_POS[12];\n  fprState->v13 = fs->V_POS[13];\n  fprState->v14 = fs->V_POS[14];\n  fprState->v15 = fs->V_POS[15];\n  fprState->v16 = fs->V_POS[16];\n  fprState->v17 = fs->V_POS[17];\n  fprState->v18 = fs->V_POS[18];\n  fprState->v19 = fs->V_POS[19];\n  fprState->v20 = fs->V_POS[20];\n  fprState->v21 = fs->V_POS[21];\n  fprState->v22 = fs->V_POS[22];\n  fprState->v23 = fs->V_POS[23];\n  fprState->v24 = fs->V_POS[24];\n  fprState->v25 = fs->V_POS[25];\n  fprState->v26 = fs->V_POS[26];\n  fprState->v27 = fs->V_POS[27];\n  fprState->v28 = fs->V_POS[28];\n  fprState->v29 = fs->V_POS[29];\n  fprState->v30 = fs->V_POS[30];\n  fprState->v31 = fs->V_POS[31];\n  fprState->fpsr = fs->FPSR_POS;\n  fprState->fpcr = fs->FPCR_POS;\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/AARCH64/macos_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_X86_H\n#define QBDIPRELOAD_X86_H\n\n#include <QBDI.h>\n\n#include <mach/thread_status.h>\n\nstatic const uint32_t BRK_INS = 0xD4200000; // brk 3ff;\n\n#define MACH_HEADER mach_header_64\n#define MACH_MAGIC MH_MAGIC_64\n#define MACH_SEG segment_command_64\n#define MACH_SEG_CMD LC_SEGMENT_64\n\n#define THREAD_STATE arm_thread_state64_t\n#define THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT\n#define THREAD_STATE_FP arm_neon_state64_t\n#define THREAD_STATE_FP_COUNT ARM_NEON_STATE64_COUNT\n#define THREAD_STATE_FP_ID ARM_NEON_STATE64\n#define THREAD_STATE_ID ARM_THREAD_STATE64\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\nstatic inline rword getReturnAddress(GPRState *gprState) {\n  return QBDI_GPR_GET(gprState, REG_LR);\n}\n\nstatic inline rword getPC(THREAD_STATE *state) {\n  return __darwin_arm_thread_state64_get_pc(*state);\n}\n\nstatic inline void setPC(THREAD_STATE *state, rword address) {\n  __darwin_arm_thread_state64_set_pc_fptr(*state, (void *)address);\n}\n\nstatic inline void prepareStack(void *newStack, size_t sizeStack,\n                                THREAD_STATE *state) {\n  __darwin_arm_thread_state64_set_sp(*state, (rword)newStack + sizeStack - 16);\n  __darwin_arm_thread_state64_set_fp(*state, (rword)newStack + sizeStack - 16);\n}\n\nstatic inline void fixSignalPC(THREAD_STATE *state) {}\n\n#endif // QBDIPRELOAD_X86_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/ARM/CMakeLists.txt",
    "content": "if(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_ARM.c\")\nelse()\n  message(FATAL_ERROR \"No QBDIPreload for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "tools/QBDIPreload/src/ARM/linux_ARM.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdlib.h>\n\n#include \"linux_ARM.h\"\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  const ucontext_t *uap = (const ucontext_t *)gprCtx;\n\n  gprState->r0 = uap->uc_mcontext.arm_r0;\n  gprState->r1 = uap->uc_mcontext.arm_r1;\n  gprState->r2 = uap->uc_mcontext.arm_r2;\n  gprState->r3 = uap->uc_mcontext.arm_r3;\n  gprState->r4 = uap->uc_mcontext.arm_r4;\n  gprState->r5 = uap->uc_mcontext.arm_r5;\n  gprState->r6 = uap->uc_mcontext.arm_r6;\n  gprState->r7 = uap->uc_mcontext.arm_r7;\n  gprState->r8 = uap->uc_mcontext.arm_r8;\n  gprState->r9 = uap->uc_mcontext.arm_r9;\n  gprState->r10 = uap->uc_mcontext.arm_r10;\n  gprState->r11 = uap->uc_mcontext.arm_fp;\n  gprState->r12 = uap->uc_mcontext.arm_ip;\n  gprState->sp = uap->uc_mcontext.arm_sp;\n  gprState->lr = uap->uc_mcontext.arm_lr;\n  gprState->pc = uap->uc_mcontext.arm_pc;\n  if (((uap->uc_mcontext.arm_cpsr >> 5) & 1) == 1) {\n    gprState->pc |= 1;\n  }\n  gprState->cpsr = uap->uc_mcontext.arm_cpsr & 0xf80f001f;\n}\n\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState) {\n  // Somehow not available under ARM\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap) {\n  uap->uc_mcontext.arm_sp = (rword)newStack + sizeStack - 8;\n  uap->uc_mcontext.arm_fp = (rword)newStack + sizeStack - 8;\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size) {\n  size_t i;\n  for (i = 0; i < size; i++) {\n    if ((modules[i].permission & QBDI_PF_EXEC) &&\n        (strstr(modules[i].name, \"libc-2.\") ||\n         strstr(modules[i].name, \"libc.so.\") ||\n         strstr(modules[i].name, \"ld-2.\") ||\n         strstr(modules[i].name, \"ld-linux-\") ||\n         strstr(modules[i].name, \"libpthread-\") ||\n         strstr(modules[i].name, \"libcofi\") || modules[i].name[0] == 0)) {\n      qbdi_removeInstrumentedRange(vm, modules[i].start, modules[i].end);\n    }\n  }\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/ARM/linux_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_ARM_H\n#define QBDIPRELOAD_ARM_H\n\n#include <dlfcn.h>\n#include <signal.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\n#define SIGBRK SIGTRAP\n\nstatic inline void *correctAddress(void *address, long *bytecode, long *mask) {\n  if ((((rword)address) & 1) == 0) {\n    *bytecode = 0xE7F001F0;\n    *mask = 0xFFFFFFFF;\n    return address;\n  } else {\n    // address is Thumb\n    *bytecode = 0xDE01;\n    *mask = 0xFFFF;\n    return (void *)(((rword)address) & ~1);\n  }\n}\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\nstatic inline rword getReturnAddress(GPRState *gprState) {\n  return QBDI_GPR_GET(gprState, REG_LR);\n}\n\nstatic inline void fix_ucontext_t(ucontext_t *uap) {\n  // nothing to do\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap);\n\nstatic inline void setPC(ucontext_t *uap, rword address) {\n  uap->uc_mcontext.arm_pc = (address & (~1));\n  if ((address & 1) == 1) {\n    uap->uc_mcontext.arm_cpsr |= (1 << 5);\n  } else {\n    uap->uc_mcontext.arm_cpsr &= ~(1 << 5);\n  }\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size);\n\n#endif // QBDIPRELOAD_ARM_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/CMakeLists.txt",
    "content": "if(QBDI_PLATFORM_LINUX)\n  target_sources(QBDIPreload\n                 PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_preload.c\")\nelseif(QBDI_PLATFORM_MACOS)\n  # Generate MIG interface at config time in the build directory\n  add_custom_command(\n    OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/mach_excServer.c\"\n    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n    COMMAND mig \"${CMAKE_CURRENT_LIST_DIR}/darwin_exception.defs\"\n    DEPENDS \"${CMAKE_CURRENT_LIST_DIR}/darwin_exception.defs\"\n    VERBATIM)\n  target_sources(\n    QBDIPreload\n    PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/darwin_preload.c\"\n            \"${CMAKE_CURRENT_LIST_DIR}/darwin_exceptd.c\"\n            \"${CMAKE_CURRENT_BINARY_DIR}/mach_excServer.c\")\nelseif(QBDI_PLATFORM_WINDOWS)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/win_preload.c\")\nelse()\n  message(FATAL_ERROR \"No QBDIPreload for platform ${QBDI_PLATFORM}\")\nendif()\n\nif(QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86/CMakeLists.txt",
    "content": "if(QBDI_PLATFORM_LINUX)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_X86.c\")\nelseif(QBDI_PLATFORM_MACOS)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/macos_X86.c\")\nelseif(QBDI_PLATFORM_WINDOWS)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/win_x86.asm\")\n  set_source_files_properties(\"${CMAKE_CURRENT_LIST_DIR}/win_x86.asm\"\n                              PROPERTIES COMPILE_FLAGS \"/safeseh\")\nelse()\n  message(FATAL_ERROR \"No QBDIPreload for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86/linux_X86.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdlib.h>\n\n#include \"linux_X86.h\"\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  const ucontext_t *uap = (const ucontext_t *)gprCtx;\n\n  gprState->eax = uap->uc_mcontext.gregs[REG_EAX];\n  gprState->ebx = uap->uc_mcontext.gregs[REG_EBX];\n  gprState->ecx = uap->uc_mcontext.gregs[REG_ECX];\n  gprState->edx = uap->uc_mcontext.gregs[REG_EDX];\n  gprState->esi = uap->uc_mcontext.gregs[REG_ESI];\n  gprState->edi = uap->uc_mcontext.gregs[REG_EDI];\n  gprState->ebp = uap->uc_mcontext.gregs[REG_EBP];\n  gprState->esp = uap->uc_mcontext.gregs[REG_ESP];\n  gprState->eip = uap->uc_mcontext.gregs[REG_EIP];\n  gprState->eflags = uap->uc_mcontext.gregs[REG_EFL];\n}\n\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState) {\n  const ucontext_t *uap = (const ucontext_t *)fprCtx;\n\n  memcpy(&fprState->stmm0, &uap->uc_mcontext.fpregs->_st[0], 10);\n  memcpy(&fprState->stmm1, &uap->uc_mcontext.fpregs->_st[1], 10);\n  memcpy(&fprState->stmm2, &uap->uc_mcontext.fpregs->_st[2], 10);\n  memcpy(&fprState->stmm3, &uap->uc_mcontext.fpregs->_st[3], 10);\n  memcpy(&fprState->stmm4, &uap->uc_mcontext.fpregs->_st[4], 10);\n  memcpy(&fprState->stmm5, &uap->uc_mcontext.fpregs->_st[5], 10);\n  memcpy(&fprState->stmm6, &uap->uc_mcontext.fpregs->_st[6], 10);\n  memcpy(&fprState->stmm7, &uap->uc_mcontext.fpregs->_st[7], 10);\n  fprState->rfcw = uap->uc_mcontext.fpregs->cw;\n  fprState->rfsw = uap->uc_mcontext.fpregs->sw;\n  fprState->ftw = 0;\n  int i, j = uap->uc_mcontext.fpregs->tag;\n  for (i = 0; i < 8; i++) {\n    if (((j >> (i * 2)) & 0x3) != 0x3) {\n      fprState->ftw |= 1 << i;\n    }\n  }\n  fprState->mxcsr = 0x1f80;\n  fprState->mxcsrmask = 0xffff;\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap) {\n  // need to copy stack arguments\n  // main have only three arguments (int argc, char** argv, char** envp) => 0xc\n  // align on 0x10\n  memcpy(newStack + sizeStack - 0x10,\n         (void *)(uap->uc_mcontext.gregs[REG_ESP] + 0x4), 0x10);\n  uap->uc_mcontext.gregs[REG_ESP] = (rword)newStack + sizeStack - 0x14;\n  uap->uc_mcontext.gregs[REG_EBP] = (rword)newStack + sizeStack - 0x14;\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size) {\n  size_t i;\n  for (i = 0; i < size; i++) {\n    if ((modules[i].permission & QBDI_PF_EXEC) &&\n        (strstr(modules[i].name, \"libc-2.\") ||\n         strstr(modules[i].name, \"libc.so.\") ||\n         strstr(modules[i].name, \"ld-2.\") ||\n         strstr(modules[i].name, \"ld-linux-\") ||\n         strstr(modules[i].name, \"libpthread-\") ||\n         strstr(modules[i].name, \"libcofi\") || modules[i].name[0] == 0)) {\n      qbdi_removeInstrumentedRange(vm, modules[i].start, modules[i].end);\n    }\n  }\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86/linux_X86.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_X86_H\n#define QBDIPRELOAD_X86_H\n\n#include <dlfcn.h>\n#include <signal.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\n#define SIGBRK SIGTRAP\n\nstatic inline void *correctAddress(void *address, long *bytecode, long *mask) {\n  *bytecode = 0xCC;\n  *mask = 0xFF;\n  return address;\n}\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\nstatic inline rword getReturnAddress(GPRState *gprState) {\n  return *((rword *)QBDI_GPR_GET(gprState, REG_SP));\n}\n\nstatic inline void fix_ucontext_t(ucontext_t *uap) {\n  uap->uc_mcontext.gregs[REG_EIP] -= 1;\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap);\n\nstatic inline void setPC(ucontext_t *uap, rword address) {\n  uap->uc_mcontext.gregs[REG_EIP] = address;\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size);\n\n#endif // QBDIPRELOAD_X86_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86/macos_X86.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"darwin_exceptd.h\"\n#include \"QBDIPreload.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  const x86_thread_state32_t *ts = (x86_thread_state32_t *)gprCtx;\n\n  gprState->eax = ts->__eax;\n  gprState->ebx = ts->__ebx;\n  gprState->ecx = ts->__ecx;\n  gprState->edx = ts->__edx;\n  gprState->esi = ts->__esi;\n  gprState->edi = ts->__edi;\n  gprState->ebp = ts->__ebp;\n  gprState->esp = ts->__esp;\n  gprState->eip = ts->__eip;\n  gprState->eflags = ts->__eflags;\n}\n\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState) {\n  const x86_float_state32_t *fs = (x86_float_state32_t *)fprCtx;\n\n#define SYNC_STMM(ID) memcpy(&fprState->stmm##ID, &fs->__fpu_stmm##ID, 10);\n  SYNC_STMM(0);\n  SYNC_STMM(1);\n  SYNC_STMM(2);\n  SYNC_STMM(3);\n  SYNC_STMM(4);\n  SYNC_STMM(5);\n  SYNC_STMM(6);\n  SYNC_STMM(7);\n#undef SYNC_STMM\n#define SYNC_XMM(ID) memcpy(&fprState->xmm##ID, &fs->__fpu_xmm##ID, 16);\n  SYNC_XMM(0);\n  SYNC_XMM(1);\n  SYNC_XMM(2);\n  SYNC_XMM(3);\n  SYNC_XMM(4);\n  SYNC_XMM(5);\n  SYNC_XMM(6);\n  SYNC_XMM(7);\n#undef SYNC_XMM\n  memcpy(&fprState->rfcw, &fs->__fpu_fcw, 2);\n  memcpy(&fprState->rfsw, &fs->__fpu_fsw, 2);\n  memcpy(&fprState->ftw, &fs->__fpu_ftw, 1);\n  memcpy(&fprState->rsrv1, &fs->__fpu_rsrv1, 1);\n  memcpy(&fprState->fop, &fs->__fpu_fop, 2);\n  memcpy(&fprState->mxcsr, &fs->__fpu_mxcsr, 4);\n  memcpy(&fprState->mxcsrmask, &fs->__fpu_mxcsrmask, 4);\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86/macos_X86.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_X86_H\n#define QBDIPRELOAD_X86_H\n\n#include <QBDI.h>\n\n#include <mach/thread_status.h>\n\nstatic const uint8_t BRK_INS = 0xCC;\n\n#define MACH_HEADER mach_header\n#define MACH_MAGIC MH_MAGIC\n#define MACH_SEG segment_command\n#define MACH_SEG_CMD LC_SEGMENT\n#define THREAD_STATE x86_thread_state32_t\n#define THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT\n#define THREAD_STATE_FP x86_float_state32_t\n#define THREAD_STATE_FP_COUNT x86_FLOAT_STATE32_COUNT\n#define THREAD_STATE_FP_ID x86_FLOAT_STATE32\n#define THREAD_STATE_ID x86_THREAD_STATE32\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\nstatic inline rword getReturnAddress(GPRState *gprState) {\n  return *((rword *)QBDI_GPR_GET(gprState, REG_SP));\n}\n\nstatic inline rword getPC(THREAD_STATE *state) { return state->__rip; }\n\nstatic inline void setPC(THREAD_STATE *state, rword address) {\n  state->__eip = address;\n}\n\nstatic inline void prepareStack(void *newStack, size_t sizeStack,\n                                THREAD_STATE *state) {\n  state->__ebp = (rword)newStack + sizeStack - 8;\n  state->__esp = state->__ebp;\n}\n\nstatic inline void fixSignalPC(THREAD_STATE *state) { state->__eip -= 1; }\n\n#endif // QBDIPRELOAD_X86_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86/win_x86.asm",
    "content": "; This file is part of QBDI.\n;\n; Copyright 2019 Quarkslab\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\n.model flat\n.code\n\n; Assembly exported trampoline\nPUBLIC _qbdipreload_trampoline \n\n; C trampoline destination import\nEXTERN _qbdipreload_trampoline_impl : proc\n\n; Original app RSP (stack top) value when\n; entering start (PE EntryPoint)\nEXTERN _g_shadowStackTop : DWORD\nEXTERN _g_shadowStackBase : DWORD\n\n; Trampoline stub\n; Update ESP/EBP and jump to trampoline implementation\n; (in C), _g_shadowStackTop & _g_shadowStackBase must be \n; intiialized before using the trampoline\n_qbdipreload_trampoline PROC\n    mov esp, _g_shadowStackTop\n    mov ebp, _g_shadowStackBase\n    jmp _qbdipreload_trampoline_impl\n_qbdipreload_trampoline ENDP\n\nEND"
  },
  {
    "path": "tools/QBDIPreload/src/X86_64/CMakeLists.txt",
    "content": "if(QBDI_PLATFORM_LINUX)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_X86_64.c\")\nelseif(QBDI_PLATFORM_MACOS)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/macos_X86_64.c\")\nelseif(QBDI_PLATFORM_WINDOWS)\n  target_sources(QBDIPreload PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/win_X86_64.asm\")\nelse()\n  message(FATAL_ERROR \"No QBDIPreload for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86_64/linux_X86_64.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <stdlib.h>\n\n#include \"linux_X86_64.h\"\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  const ucontext_t *uap = (const ucontext_t *)gprCtx;\n\n  gprState->rax = uap->uc_mcontext.gregs[REG_RAX];\n  gprState->rbx = uap->uc_mcontext.gregs[REG_RBX];\n  gprState->rcx = uap->uc_mcontext.gregs[REG_RCX];\n  gprState->rdx = uap->uc_mcontext.gregs[REG_RDX];\n  gprState->rsi = uap->uc_mcontext.gregs[REG_RSI];\n  gprState->rdi = uap->uc_mcontext.gregs[REG_RDI];\n  gprState->rbp = uap->uc_mcontext.gregs[REG_RBP];\n  gprState->rsp = uap->uc_mcontext.gregs[REG_RSP];\n  gprState->r8 = uap->uc_mcontext.gregs[REG_R8];\n  gprState->r9 = uap->uc_mcontext.gregs[REG_R9];\n  gprState->r10 = uap->uc_mcontext.gregs[REG_R10];\n  gprState->r11 = uap->uc_mcontext.gregs[REG_R11];\n  gprState->r12 = uap->uc_mcontext.gregs[REG_R12];\n  gprState->r13 = uap->uc_mcontext.gregs[REG_R13];\n  gprState->r14 = uap->uc_mcontext.gregs[REG_R14];\n  gprState->r15 = uap->uc_mcontext.gregs[REG_R15];\n  gprState->rip = uap->uc_mcontext.gregs[REG_RIP];\n  gprState->eflags = uap->uc_mcontext.gregs[REG_EFL];\n}\n\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState) {\n  const ucontext_t *uap = (const ucontext_t *)fprCtx;\n\n  memcpy(&fprState->stmm0, &uap->uc_mcontext.fpregs->_st[0], 10);\n  memcpy(&fprState->stmm1, &uap->uc_mcontext.fpregs->_st[1], 10);\n  memcpy(&fprState->stmm2, &uap->uc_mcontext.fpregs->_st[2], 10);\n  memcpy(&fprState->stmm3, &uap->uc_mcontext.fpregs->_st[3], 10);\n  memcpy(&fprState->stmm4, &uap->uc_mcontext.fpregs->_st[4], 10);\n  memcpy(&fprState->stmm5, &uap->uc_mcontext.fpregs->_st[5], 10);\n  memcpy(&fprState->stmm6, &uap->uc_mcontext.fpregs->_st[6], 10);\n  memcpy(&fprState->stmm7, &uap->uc_mcontext.fpregs->_st[7], 10);\n  memcpy(&fprState->xmm0, &uap->uc_mcontext.fpregs->_xmm[0], 16);\n  memcpy(&fprState->xmm1, &uap->uc_mcontext.fpregs->_xmm[1], 16);\n  memcpy(&fprState->xmm2, &uap->uc_mcontext.fpregs->_xmm[2], 16);\n  memcpy(&fprState->xmm3, &uap->uc_mcontext.fpregs->_xmm[3], 16);\n  memcpy(&fprState->xmm4, &uap->uc_mcontext.fpregs->_xmm[4], 16);\n  memcpy(&fprState->xmm5, &uap->uc_mcontext.fpregs->_xmm[5], 16);\n  memcpy(&fprState->xmm6, &uap->uc_mcontext.fpregs->_xmm[6], 16);\n  memcpy(&fprState->xmm7, &uap->uc_mcontext.fpregs->_xmm[7], 16);\n  memcpy(&fprState->xmm8, &uap->uc_mcontext.fpregs->_xmm[8], 16);\n  memcpy(&fprState->xmm9, &uap->uc_mcontext.fpregs->_xmm[9], 16);\n  memcpy(&fprState->xmm10, &uap->uc_mcontext.fpregs->_xmm[10], 16);\n  memcpy(&fprState->xmm11, &uap->uc_mcontext.fpregs->_xmm[11], 16);\n  memcpy(&fprState->xmm12, &uap->uc_mcontext.fpregs->_xmm[12], 16);\n  memcpy(&fprState->xmm13, &uap->uc_mcontext.fpregs->_xmm[13], 16);\n  memcpy(&fprState->xmm14, &uap->uc_mcontext.fpregs->_xmm[14], 16);\n  memcpy(&fprState->xmm15, &uap->uc_mcontext.fpregs->_xmm[15], 16);\n  fprState->rfcw = uap->uc_mcontext.fpregs->cwd;\n  fprState->rfsw = uap->uc_mcontext.fpregs->swd;\n  fprState->ftw = uap->uc_mcontext.fpregs->ftw & 0xFF;\n  fprState->rsrv1 = uap->uc_mcontext.fpregs->ftw >> 8;\n  fprState->fop = uap->uc_mcontext.fpregs->fop;\n  fprState->mxcsr = uap->uc_mcontext.fpregs->mxcsr;\n  fprState->mxcsrmask = uap->uc_mcontext.fpregs->mxcr_mask;\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap) {\n  uap->uc_mcontext.gregs[REG_RSP] = (rword)newStack + sizeStack - 8;\n  uap->uc_mcontext.gregs[REG_RBP] = (rword)newStack + sizeStack - 8;\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size) {\n  size_t i;\n  for (i = 0; i < size; i++) {\n    if ((modules[i].permission & QBDI_PF_EXEC) &&\n        (strstr(modules[i].name, \"libc-2.\") ||\n         strstr(modules[i].name, \"libc.so.\") ||\n         strstr(modules[i].name, \"ld-2.\") ||\n         strstr(modules[i].name, \"ld-linux-\") ||\n         strstr(modules[i].name, \"libpthread-\") ||\n         strstr(modules[i].name, \"libcofi\") || modules[i].name[0] == 0)) {\n      qbdi_removeInstrumentedRange(vm, modules[i].start, modules[i].end);\n    }\n  }\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86_64/linux_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_X86_64_H\n#define QBDIPRELOAD_X86_64_H\n\n#include <dlfcn.h>\n#include <signal.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\n#define SIGBRK SIGTRAP\n\nstatic inline void *correctAddress(void *address, long *bytecode, long *mask) {\n  *bytecode = 0xCC;\n  *mask = 0xFF;\n  return address;\n}\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\nstatic inline rword getReturnAddress(GPRState *gprState) {\n  return *((rword *)QBDI_GPR_GET(gprState, REG_SP));\n}\n\nstatic inline void fix_ucontext_t(ucontext_t *uap) {\n  uap->uc_mcontext.gregs[REG_RIP] -= 1;\n}\n\nvoid prepareStack(void *newStack, size_t sizeStack, ucontext_t *uap);\n\nstatic inline void setPC(ucontext_t *uap, rword address) {\n  uap->uc_mcontext.gregs[REG_RIP] = address;\n}\n\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size);\n\n#endif // QBDIPRELOAD_X86_64_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86_64/macos_X86_64.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"macos_X86_64.h\"\n#include \"QBDIPreload.h\"\n\n#include <mach/thread_act.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  const x86_thread_state64_t *ts = (x86_thread_state64_t *)gprCtx;\n\n  gprState->rax = ts->__rax;\n  gprState->rbx = ts->__rbx;\n  gprState->rcx = ts->__rcx;\n  gprState->rdx = ts->__rdx;\n  gprState->rsi = ts->__rsi;\n  gprState->rdi = ts->__rdi;\n  gprState->rbp = ts->__rbp;\n  gprState->rsp = ts->__rsp;\n  gprState->r8 = ts->__r8;\n  gprState->r9 = ts->__r9;\n  gprState->r10 = ts->__r10;\n  gprState->r11 = ts->__r11;\n  gprState->r12 = ts->__r12;\n  gprState->r13 = ts->__r13;\n  gprState->r14 = ts->__r14;\n  gprState->r15 = ts->__r15;\n  gprState->rip = ts->__rip;\n  gprState->eflags = ts->__rflags;\n}\n\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState) {\n  const x86_float_state64_t *fs = (x86_float_state64_t *)fprCtx;\n\n#define SYNC_STMM(ID) memcpy(&fprState->stmm##ID, &fs->__fpu_stmm##ID, 10);\n  SYNC_STMM(0);\n  SYNC_STMM(1);\n  SYNC_STMM(2);\n  SYNC_STMM(3);\n  SYNC_STMM(4);\n  SYNC_STMM(5);\n  SYNC_STMM(6);\n  SYNC_STMM(7);\n#undef SYNC_STMM\n#define SYNC_XMM(ID) memcpy(&fprState->xmm##ID, &fs->__fpu_xmm##ID, 16);\n  SYNC_XMM(0);\n  SYNC_XMM(1);\n  SYNC_XMM(2);\n  SYNC_XMM(3);\n  SYNC_XMM(4);\n  SYNC_XMM(5);\n  SYNC_XMM(6);\n  SYNC_XMM(7);\n  SYNC_XMM(8);\n  SYNC_XMM(9);\n  SYNC_XMM(10);\n  SYNC_XMM(11);\n  SYNC_XMM(12);\n  SYNC_XMM(13);\n  SYNC_XMM(14);\n  SYNC_XMM(15);\n#undef SYNC_XMM\n  memcpy(&fprState->rfcw, &fs->__fpu_fcw, 2);\n  memcpy(&fprState->rfsw, &fs->__fpu_fsw, 2);\n  memcpy(&fprState->ftw, &fs->__fpu_ftw, 1);\n  memcpy(&fprState->rsrv1, &fs->__fpu_rsrv1, 1);\n  memcpy(&fprState->fop, &fs->__fpu_fop, 2);\n  memcpy(&fprState->mxcsr, &fs->__fpu_mxcsr, 4);\n  memcpy(&fprState->mxcsrmask, &fs->__fpu_mxcsrmask, 4);\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86_64/macos_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef QBDIPRELOAD_X86_H\n#define QBDIPRELOAD_X86_H\n\n#include <QBDI.h>\n\n#include <mach/thread_status.h>\n\nstatic const uint8_t BRK_INS = 0xCC;\n\n#define MACH_HEADER mach_header_64\n#define MACH_MAGIC MH_MAGIC_64\n#define MACH_SEG segment_command_64\n#define MACH_SEG_CMD LC_SEGMENT_64\n#define THREAD_STATE x86_thread_state64_t\n#define THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT\n#define THREAD_STATE_FP x86_float_state64_t\n#define THREAD_STATE_FP_COUNT x86_FLOAT_STATE64_COUNT\n#define THREAD_STATE_FP_ID x86_FLOAT_STATE64\n#define THREAD_STATE_ID x86_THREAD_STATE64\n\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState);\nvoid qbdipreload_floatCtxToFPRState(const void *fprCtx, FPRState *fprState);\n\nstatic inline rword getReturnAddress(GPRState *gprState) {\n  return *((rword *)QBDI_GPR_GET(gprState, REG_SP));\n}\n\nstatic inline rword getPC(THREAD_STATE *state) { return state->__rip; }\n\nstatic inline void setPC(THREAD_STATE *state, rword address) {\n  state->__rip = address;\n}\n\nstatic inline void prepareStack(void *newStack, size_t sizeStack,\n                                THREAD_STATE *state) {\n  state->__rbp = (rword)newStack + sizeStack - 8;\n  state->__rsp = state->__rbp;\n}\n\nstatic inline void fixSignalPC(THREAD_STATE *state) { state->__rip -= 1; }\n\n#endif // QBDIPRELOAD_X86_H\n"
  },
  {
    "path": "tools/QBDIPreload/src/X86_64/win_X86_64.asm",
    "content": "; This file is part of QBDI.\n;\n; Copyright 2019 Quarkslab\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\n.code\n\n; Assembly exported trampoline\nPUBLIC qbdipreload_trampoline \n\n; C trampoline destination import\nEXTERN qbdipreload_trampoline_impl : proc\n\n; Original app RSP (stack top) value when\n; entering start (PE EntryPoint)\nEXTERN g_shadowStackTop : QWORD\n\n; Trampoline stub\n; Update RSP and jump to trampoline implementation\n; (in C), g_shadowStackTop must be intiialized before\n; using the trampoline\n; Note that RBP is not used on x64\nqbdipreload_trampoline PROC\n    mov rsp, g_shadowStackTop\n    jmp qbdipreload_trampoline_impl\nqbdipreload_trampoline ENDP\n\nEND"
  },
  {
    "path": "tools/QBDIPreload/src/darwin_exceptd.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <mach/mach_error.h>\n#include <mach/mach_init.h>\n#include <mach/mach_port.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"darwin_exceptd.h\"\n#include \"mach_exc.h\"\n\nstruct ExceptionHandler {\n  task_t target;\n  mach_port_t port;\n  exception_mask_t mask;\n  exception_handler_func handler;\n  bool running;\n};\n\nstatic pthread_mutex_t REGISTERED_HANDLERS_LOCK = PTHREAD_MUTEX_INITIALIZER;\n\nstatic struct ExceptionHandler **REGISTERED_HANDLERS = NULL;\nstatic size_t REGISTERED_HANDLERS_COUNT = 0;\nstatic size_t REGISTERED_HANDLERS_CAPACITY = 0;\n\nkern_return_t catch_mach_exception_raise(mach_port_t exception_port,\n                                         mach_port_t thread, mach_port_t task,\n                                         exception_type_t exception,\n                                         mach_exception_data_t code,\n                                         mach_msg_type_number_t codeCnt) {\n  exception_handler_func forward = NULL;\n  pthread_mutex_lock(&REGISTERED_HANDLERS_LOCK);\n  for (size_t i = 0; i < REGISTERED_HANDLERS_COUNT; i++) {\n    if (exception_port == REGISTERED_HANDLERS[i]->port) {\n      forward = REGISTERED_HANDLERS[i]->handler;\n      break;\n    }\n  }\n  pthread_mutex_unlock(&REGISTERED_HANDLERS_LOCK);\n\n  if (forward != NULL) {\n    return forward(exception_port, thread, task, exception, code, codeCnt);\n  }\n\n  fprintf(stderr, \"Exception handler not found!\\n\");\n  return KERN_FAILURE;\n}\n\n// Should never be called but must exist\nkern_return_t catch_mach_exception_raise_state(\n    mach_port_t exception_port, exception_type_t exception,\n    const mach_exception_data_t code, mach_msg_type_number_t codeCnt,\n    int *flavor, const thread_state_t old_state,\n    mach_msg_type_number_t old_stateCnt, thread_state_t new_state,\n    mach_msg_type_number_t *new_stateCnt) {\n  fprintf(stderr, \"catch_mach_exception_raise_state called!\");\n  return KERN_FAILURE;\n}\n\n// Should never be called but must exist\nkern_return_t catch_mach_exception_raise_state_identity(\n    mach_port_t exception_port, mach_port_t thread, mach_port_t task,\n    exception_type_t exception, mach_exception_data_t code,\n    mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state,\n    mach_msg_type_number_t old_stateCnt, thread_state_t new_state,\n    mach_msg_type_number_t *new_stateCnt) {\n  fprintf(stderr, \"catch_mach_exception_raise_state called!\");\n  return KERN_FAILURE;\n}\n\n// Undocumented function implemented in libsystem_kernel.dylib\nboolean_t mach_exc_server(mach_msg_header_t *msg, mach_msg_header_t *reply);\n\nstatic void *exception_server(void *data) {\n  kern_return_t kr;\n  mach_msg_return_t rt;\n  struct ExceptionHandler *exceptionHandler = (struct ExceptionHandler *)data;\n  mach_msg_header_t *msg = (mach_msg_header_t *)calloc(\n      1, sizeof(__Request__mach_exception_raise_state_identity_t));\n  mach_msg_header_t *reply = (mach_msg_header_t *)calloc(\n      1, sizeof(__Request__mach_exception_raise_state_identity_t));\n\n  while (exceptionHandler->running) {\n    rt = mach_msg(msg, MACH_RCV_MSG, 0,\n                  sizeof(__Request__mach_exception_raise_state_identity_t),\n                  exceptionHandler->port, 200, MACH_PORT_NULL);\n    if (rt == MACH_MSG_SUCCESS) {\n      mach_exc_server(msg, reply);\n      rt = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL,\n                    500, MACH_PORT_NULL);\n      if (rt != MACH_MSG_SUCCESS) {\n        fprintf(stderr, \"Failed to send reply on exception port: %s\\n\",\n                mach_error_string(rt));\n        break;\n      }\n    } else if (rt != MACH_RCV_TIMED_OUT) {\n      fprintf(stderr, \"Failed to receive message on exception port: %s\\n\",\n              mach_error_string(rt));\n      break;\n    }\n  }\n\n  kr = task_set_exception_ports(exceptionHandler->target,\n                                exceptionHandler->mask, MACH_PORT_NULL, 0, 0);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to deregister our exception handler\\n\");\n  }\n  kr = mach_port_deallocate(exceptionHandler->target, exceptionHandler->port);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to deallocate target exception port right\\n\");\n  }\n  kr = mach_port_deallocate(mach_task_self(), exceptionHandler->port);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to deallocate self exception port right\\n\");\n  }\n\n  if (reply)\n    free(reply);\n  if (msg)\n    free(msg);\n  if (exceptionHandler)\n    free(exceptionHandler);\n  return NULL;\n}\n\nstruct ExceptionHandler *setupExceptionHandler(task_t target,\n                                               exception_mask_t exceptionMask,\n                                               exception_handler_func handler) {\n  pthread_t tid;\n  pthread_attr_t attr;\n  kern_return_t kr;\n  struct ExceptionHandler *exceptionHandler =\n      calloc(1, sizeof(struct ExceptionHandler));\n\n  exceptionHandler->target = target;\n  exceptionHandler->port = 0;\n  exceptionHandler->mask = exceptionMask;\n  exceptionHandler->handler = handler;\n  exceptionHandler->running = true;\n\n  if (REGISTERED_HANDLERS == NULL) {\n    REGISTERED_HANDLERS_CAPACITY = 1;\n    REGISTERED_HANDLERS_COUNT = 0;\n    REGISTERED_HANDLERS = (struct ExceptionHandler **)calloc(\n        REGISTERED_HANDLERS_CAPACITY, sizeof(struct ExceptionHandler *));\n  }\n\n  kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,\n                          &exceptionHandler->port);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to allocate a port\\n\");\n    free(exceptionHandler);\n    return NULL;\n  }\n\n  kr = mach_port_insert_right(mach_task_self(), exceptionHandler->port,\n                              exceptionHandler->port, MACH_MSG_TYPE_MAKE_SEND);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to insert port right into target\\n\");\n    free(exceptionHandler);\n    return NULL;\n  }\n\n  kr = task_set_exception_ports(\n      target, exceptionHandler->mask, exceptionHandler->port,\n      EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to set exception port: %s\\n\",\n            mach_error_string(kr));\n    free(exceptionHandler);\n    return NULL;\n  }\n\n  pthread_mutex_lock(&REGISTERED_HANDLERS_LOCK);\n  if (REGISTERED_HANDLERS_COUNT == REGISTERED_HANDLERS_CAPACITY) {\n    // Realloc\n    struct ExceptionHandler **newlist = (struct ExceptionHandler **)calloc(\n        REGISTERED_HANDLERS_CAPACITY * 2, sizeof(struct ExceptionHandler *));\n    // Copy old list\n    memcpy(newlist, REGISTERED_HANDLERS,\n           REGISTERED_HANDLERS_COUNT * sizeof(struct ExceptionHandler));\n    // Free and replace\n    free(REGISTERED_HANDLERS);\n    REGISTERED_HANDLERS = newlist;\n    // Update capacity\n    REGISTERED_HANDLERS_CAPACITY *= 2;\n  }\n  REGISTERED_HANDLERS[REGISTERED_HANDLERS_COUNT] = exceptionHandler;\n  REGISTERED_HANDLERS_COUNT += 1;\n  pthread_mutex_unlock(&REGISTERED_HANDLERS_LOCK);\n\n  pthread_attr_init(&attr);\n  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);\n  pthread_create(&tid, &attr, &exception_server, exceptionHandler);\n\n  return exceptionHandler;\n}\n\nvoid stopExceptionHandler(struct ExceptionHandler *exceptionHandler) {\n  pthread_mutex_lock(&REGISTERED_HANDLERS_LOCK);\n  for (size_t i = 0; i < REGISTERED_HANDLERS_COUNT; i++) {\n    if (exceptionHandler == REGISTERED_HANDLERS[i]) {\n      // Remove and update list\n      for (size_t j = i; j < REGISTERED_HANDLERS_COUNT - 1; j++) {\n        REGISTERED_HANDLERS[j] = REGISTERED_HANDLERS[j + 1];\n      }\n      REGISTERED_HANDLERS_COUNT -= 1;\n      // Set running flag to false\n      exceptionHandler->running = false;\n      pthread_mutex_unlock(&REGISTERED_HANDLERS_LOCK);\n      return;\n    }\n  }\n  pthread_mutex_unlock(&REGISTERED_HANDLERS_LOCK);\n  fprintf(stderr, \"Exception handler not found in the registered handler\\n\");\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/darwin_exceptd.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <mach/task.h>\n#include <pthread.h>\n\ntypedef kern_return_t (*exception_handler_func)(mach_port_t, mach_port_t,\n                                                mach_port_t, exception_type_t,\n                                                mach_exception_data_t,\n                                                mach_msg_type_number_t);\n\nstruct ExceptionHandler;\n\nstruct ExceptionHandler *setupExceptionHandler(task_t target,\n                                               exception_mask_t exceptionMask,\n                                               exception_handler_func handler);\n\nvoid stopExceptionHandler(struct ExceptionHandler *exceptionHandler);\n"
  },
  {
    "path": "tools/QBDIPreload/src/darwin_exception.defs",
    "content": "#import <mach/mach_exc.defs>\n"
  },
  {
    "path": "tools/QBDIPreload/src/darwin_preload.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"darwin_exceptd.h\"\n#include \"QBDIPreload.h\"\n\n#include <mach-o/dyld.h>\n#include <mach-o/loader.h>\n#include <mach/mach.h>\n#include <mach/mach_error.h>\n#include <mach/mach_init.h>\n#include <mach/mach_port.h>\n#include <mach/mach_traps.h>\n#include <mach/mach_vm.h>\n#include <mach/task.h>\n#include <mach/thread_act.h>\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\n#if defined(QBDI_ARCH_X86)\n#include \"X86/macos_X86.h\"\n#elif defined(QBDI_ARCH_X86_64)\n#include \"X86_64/macos_X86_64.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"AARCH64/macos_AARCH64.h\"\n#else\n#error \"Architecture not supported\"\n#endif\n\n#define DYLD_INTERPOSE(_replacment, _replacee)                                \\\n  __attribute__((used)) static struct {                                       \\\n    const void *replacment;                                                   \\\n    const void *replacee;                                                     \\\n  } _interpose_##_replacee __attribute__((section(\"__DATA,__interpose\"))) = { \\\n      (const void *)(unsigned long)&_replacment,                              \\\n      (const void *)(unsigned long)&_replacee};\n\nstatic const size_t STACK_SIZE = 8388608;\n\nstatic bool HAS_EXITED = false;\nstatic bool HAS_PRELOAD = false;\nstatic bool DEFAULT_HANDLER = false;\nstatic GPRState ENTRY_GPR;\nstatic FPRState ENTRY_FPR;\n\nstatic struct ExceptionHandler *MAIN_EXCEPTION_HANDLER = NULL;\n\nstatic void writeCode(rword address, void *data, size_t data_size) {\n  kern_return_t kr;\n  vm_prot_t cur_protection, max_protection;\n  task_t self = mach_task_self();\n  int pageSize = getpagesize();\n\n  // 1. copy the page data to another pages\n  void *pageAddr = mmap(NULL, pageSize, PROT_READ | PROT_WRITE,\n                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n  if (pageAddr == MAP_FAILED) {\n    perror(\"Failed to create a new page\");\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  rword addressPage = address & ~(pageSize - 1);\n\n  memcpy(pageAddr, (void *)addressPage, pageSize);\n\n  // 2. write data\n  memcpy(pageAddr + (address & (pageSize - 1)), data, data_size);\n\n  // 3. protect page to RX\n  kr = mach_vm_protect(self, (mach_vm_address_t)pageAddr, pageSize, false,\n                       VM_PROT_READ | VM_PROT_EXECUTE);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to change memory protection to RX: %s\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  // 4. remap page\n  mach_vm_address_t remapAddr = addressPage;\n  kr = mach_vm_remap(self, &remapAddr, pageSize, 0,\n                     VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, self,\n                     (mach_vm_address_t)pageAddr, TRUE, &cur_protection,\n                     &max_protection, VM_INHERIT_COPY);\n\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to remap the page to the origin address: %s\\n\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n  if (remapAddr != addressPage) {\n    fprintf(stderr,\n            \"Remap fail to use the given address: \"\n            \"0x%\" PRIRWORD \" != 0x%\" PRIRWORD \"\\n\",\n            (rword)remapAddr, (rword)address);\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  // 5. unmap the temporary map\n  if (munmap(pageAddr, pageSize) == -1) {\n    perror(\"Failed to munmap\");\n  }\n}\n\nstatic struct {\n  rword originPageAddr;\n  rword remapPageAddr;\n  int size;\n} ENTRY_BRK;\n\nvoid setEntryBreakpoint(rword address) {\n  kern_return_t kr;\n  vm_prot_t cur_protection, max_protection;\n\n  // 1. get current mach_port_t from current task\n  mach_port_t task;\n  task_t self = mach_task_self();\n  kr = task_for_pid(self, getpid(), &task);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to get mach_port for self pid: %s\\n\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  // 2. iterate on region to find the region of address\n  uint32_t depth = 1;\n  vm_size_t size = 0;\n  vm_address_t addr = 0, next = addr;\n\n  while (1) {\n    struct vm_region_submap_info_64 basic_info;\n    mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;\n    kr = vm_region_recurse_64(task, &next, &size, &depth,\n                              (vm_region_recurse_info_t)&basic_info, &count);\n    if (kr != KERN_SUCCESS) {\n      fprintf(stderr, \"Cannot found address 0x%\" PRIRWORD \" : %s\\n\", address,\n              mach_error_string(kr));\n      exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n    }\n\n    if (basic_info.is_submap) {\n      depth++;\n      continue;\n    }\n\n    addr = next;\n    next += size;\n\n    // Found region of address\n    if (addr <= address && address < addr + size) {\n      size_t pageSize = getpagesize();\n      // align addr and size\n      ENTRY_BRK.originPageAddr = addr & ~(pageSize - 1);\n      ENTRY_BRK.size =\n          ((addr - ENTRY_BRK.originPageAddr) + size + pageSize - 1) &\n          ~(pageSize - 1);\n      break;\n    }\n  }\n\n  // 3. remap the region\n  ENTRY_BRK.remapPageAddr = 0;\n  kr = mach_vm_remap(self, &ENTRY_BRK.remapPageAddr, ENTRY_BRK.size, 0,\n                     VM_FLAGS_ANYWHERE, self, ENTRY_BRK.originPageAddr, TRUE,\n                     &cur_protection, &max_protection, VM_INHERIT_COPY);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr,\n            \"Failed to remap original page before insert breakpoint: \"\n            \"%s\\n\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  // 4. write the breakpoint\n  writeCode(address, (void *)&BRK_INS, sizeof(BRK_INS));\n}\n\nvoid unsetEntryBreakpoint() {\n  kern_return_t kr;\n  vm_prot_t cur_protection, max_protection;\n  task_t self = mach_task_self();\n\n  // 1. remap original code\n  kr = mach_vm_remap(self, &ENTRY_BRK.originPageAddr, ENTRY_BRK.size, 0,\n                     VM_FLAGS_OVERWRITE | VM_FLAGS_FIXED, self,\n                     ENTRY_BRK.remapPageAddr, TRUE, &cur_protection,\n                     &max_protection, VM_INHERIT_COPY);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr,\n            \"Failed to remap original page after inserted breakpoint: \"\n            \"%s\\n\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  // 2. remove copy\n  if (munmap((void *)ENTRY_BRK.remapPageAddr, ENTRY_BRK.size) == -1) {\n    perror(\"Failed to munmap\");\n  }\n}\n\nrword getEntrypointAddress() {\n  uint32_t imageIndex = -1;\n  bool foundImageIndex = false;\n  char *execPath = NULL;\n  char *execBaseName = NULL;\n  uint32_t execPathLen = 0;\n\n  unsigned i = 0, j = 0;\n  rword segaddr = 0;\n  rword entryoff = 0;\n  rword slide;\n  const struct MACH_HEADER *header;\n\n  // get path of the binary and extract basename\n  _NSGetExecutablePath(NULL, &execPathLen);\n  if (execPathLen <= 0) {\n    fprintf(stderr, \"Fail to get binary path\\n\");\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n  execPathLen += 1;\n  execPath = (char *)malloc(execPathLen);\n  if (execPath == NULL) {\n    fprintf(stderr, \"Buffer allocation fail\\n\");\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n  memset(execPath, '\\0', execPathLen);\n  if (_NSGetExecutablePath(execPath, &execPathLen) != 0) {\n    free(execPath);\n    fprintf(stderr, \"Fail to get binary path\\n\");\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  execBaseName = strrchr(execPath, '/');\n  if (execBaseName == NULL) {\n    execBaseName = execPath;\n  } else {\n    // skip '/'\n    execBaseName++;\n  }\n\n  // search image index that match the basename\n  while (!foundImageIndex) {\n    const char *currentImageName = _dyld_get_image_name(++imageIndex);\n    const char *currentBaseName;\n    if (currentImageName == NULL) {\n      free(execPath);\n      fprintf(stderr, \"Fail to found binary index\\n\");\n      exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n    }\n    currentBaseName = strrchr(currentImageName, '/');\n    if (currentBaseName == NULL) {\n      currentBaseName = currentImageName;\n    } else {\n      // skip '/'\n      currentBaseName++;\n    }\n    foundImageIndex = (strcmp(currentBaseName, execBaseName) == 0);\n  }\n  free(execPath);\n\n  // get header of the binary\n  slide = _dyld_get_image_vmaddr_slide(imageIndex);\n  header = (struct MACH_HEADER *)_dyld_get_image_header(imageIndex);\n\n  // Checking that it is indeed a mach binary\n  if (header->magic != MACH_MAGIC) {\n    fprintf(stderr, \"Process is not a mach binary\\n\");\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  // Find entrypoint and _TEXT segment command - Logic copied from\n  // libmacho/getsecbyname.c\n  struct load_command *cmd =\n      (struct load_command *)((char *)header + sizeof(struct MACH_HEADER));\n  for (i = 0; i < header->ncmds; i++) {\n    if (cmd->cmd == LC_UNIXTHREAD) {\n      uint32_t flavor = *((uint32_t *)cmd + 2);\n      switch (flavor) {\n        // TODO: support more arch\n        case THREAD_STATE_ID: {\n          THREAD_STATE *state = (THREAD_STATE *)((uint32_t *)cmd + 4);\n          entryoff = getPC(state);\n          return entryoff + slide;\n        }\n      }\n    }\n#ifdef LC_MAIN\n    else if (cmd->cmd == LC_MAIN) {\n      entryoff = ((struct entry_point_command *)cmd)->entryoff;\n      j |= 1;\n    }\n#endif\n    else if (cmd->cmd == MACH_SEG_CMD &&\n             strcmp(\"__TEXT\", ((struct MACH_SEG *)cmd)->segname) == 0) {\n      segaddr = ((struct MACH_SEG *)cmd)->vmaddr;\n      j |= 2;\n    }\n    cmd = (struct load_command *)((char *)cmd + cmd->cmdsize);\n  }\n\n  if (j != 3) {\n    fprintf(stderr, \"Could not find process entry point\\n\");\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  return segaddr + slide + entryoff;\n}\n\nvoid catchEntrypoint(int argc, char **argv) {\n  int status = QBDIPRELOAD_NOT_HANDLED;\n\n  unsetEntryBreakpoint();\n  stopExceptionHandler(MAIN_EXCEPTION_HANDLER);\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  // detect legacy UNIXTHREAD start (push 0)\n  uint16_t *insn = (uint16_t *)QBDI_GPR_GET(&ENTRY_GPR, REG_PC);\n  if (*insn == 0x6a) {\n    argc = *(int *)QBDI_GPR_GET(&ENTRY_GPR, REG_SP);\n    argv = (char **)((rword)QBDI_GPR_GET(&ENTRY_GPR, REG_SP) + sizeof(rword));\n  }\n#endif\n  status = qbdipreload_on_main(argc, argv);\n\n  if (DEFAULT_HANDLER && (status == QBDIPRELOAD_NOT_HANDLED)) {\n    VMInstanceRef vm;\n    qbdi_initVM(&vm, NULL, NULL, 0);\n    qbdi_instrumentAllExecutableMaps(vm);\n\n    // Skip system library (to avoid conflicts)\n    size_t size = 0, i = 0;\n    char **modules = qbdi_getModuleNames(&size);\n\n    // Filter some modules to avoid conflicts\n    qbdi_removeInstrumentedModuleFromAddr(vm, (rword)&catchEntrypoint);\n    for (i = 0; i < size; i++) {\n      if (strstr(modules[i], \"libsystem\")) {\n        qbdi_removeInstrumentedModule(vm, modules[i]);\n      }\n    }\n    for (i = 0; i < size; i++) {\n      free(modules[i]);\n    }\n    free(modules);\n\n    // Setup VM states\n    qbdi_setGPRState(vm, &ENTRY_GPR);\n    qbdi_setFPRState(vm, &ENTRY_FPR);\n\n    rword start = QBDI_GPR_GET(qbdi_getGPRState(vm), REG_PC);\n    rword stop = getReturnAddress(qbdi_getGPRState(vm));\n    status = qbdipreload_on_run(vm, start, stop);\n  }\n  exit(status);\n}\n\nkern_return_t redirectExec(mach_port_t exception_port, mach_port_t thread,\n                           mach_port_t task, exception_type_t exception,\n                           mach_exception_data_t code,\n                           mach_msg_type_number_t codeCnt) {\n  kern_return_t kr;\n  THREAD_STATE threadState;\n  THREAD_STATE_FP floatState;\n  mach_msg_type_number_t count;\n\n  // Reading thread state\n  count = THREAD_STATE_COUNT;\n  kr = thread_get_state(thread, THREAD_STATE_ID, (thread_state_t)&threadState,\n                        &count);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to get GPR thread state: %s\\n\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n  count = THREAD_STATE_FP_COUNT;\n  kr = thread_get_state(thread, THREAD_STATE_FP_ID, (thread_state_t)&floatState,\n                        &count);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to get FPR thread state: %s\\n\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  // x86 breakpoint quirk\n  fixSignalPC(&threadState);\n\n  int status =\n      qbdipreload_on_premain((void *)&threadState, (void *)&floatState);\n\n  // Copying the initial thread state\n  qbdipreload_threadCtxToGPRState(&threadState, &ENTRY_GPR);\n  qbdipreload_floatCtxToFPRState(&floatState, &ENTRY_FPR);\n\n  // if not handled, use default handler\n  if (status == QBDIPRELOAD_NOT_HANDLED) {\n    DEFAULT_HANDLER = true;\n\n    // Allocating fake stack\n    void *newStack = NULL;\n    kr =\n        mach_vm_map(task, (mach_vm_address_t *)&newStack, STACK_SIZE, 0,\n                    VM_FLAGS_ANYWHERE, MEMORY_OBJECT_NULL, 0, false,\n                    VM_PROT_READ | VM_PROT_WRITE, VM_PROT_ALL, VM_INHERIT_COPY);\n    if (kr != KERN_SUCCESS) {\n      fprintf(stderr, \"Failed to allocate fake stack: %s\\n\",\n              mach_error_string(kr));\n      exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n    }\n\n    // Swapping to fake stack\n    prepareStack(newStack, STACK_SIZE, &threadState);\n  }\n\n  // Execution redirection\n  setPC(&threadState, (rword)catchEntrypoint);\n  count = THREAD_STATE_COUNT;\n  kr = thread_set_state(thread, THREAD_STATE_ID, (thread_state_t)&threadState,\n                        count);\n  if (kr != KERN_SUCCESS) {\n    fprintf(stderr, \"Failed to set GPR thread state for redirection: %s\\n\",\n            mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n  return KERN_SUCCESS;\n}\n\nvoid *qbdipreload_setup_exception_handler(uint32_t target, uint32_t mask,\n                                          void *handler) {\n  task_t target_ = (task_t)target;\n  exception_mask_t mask_ = (exception_mask_t)mask;\n  exception_handler_func handler_ = (exception_handler_func)handler;\n  return setupExceptionHandler(target_, mask_, handler_);\n}\n\nint qbdipreload_hook_main(void *main) {\n  setEntryBreakpoint((rword)main);\n  MAIN_EXCEPTION_HANDLER = setupExceptionHandler(\n      mach_task_self(), EXC_MASK_BREAKPOINT, redirectExec);\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nQBDI_FORCE_EXPORT void intercept_exit(int status) {\n  if (!HAS_EXITED && HAS_PRELOAD) {\n    HAS_EXITED = true;\n    qbdipreload_on_exit(status);\n  }\n  exit(status);\n}\nDYLD_INTERPOSE(intercept_exit, exit)\n\nQBDI_FORCE_EXPORT void intercept__exit(int status) {\n  if (!HAS_EXITED && HAS_PRELOAD) {\n    HAS_EXITED = true;\n    qbdipreload_on_exit(status);\n  }\n  _exit(status);\n}\nDYLD_INTERPOSE(intercept__exit, _exit)\n\nint qbdipreload_hook_init() {\n  // do nothing if the library isn't preload\n  if (getenv(\"DYLD_INSERT_LIBRARIES\") == NULL)\n    return QBDIPRELOAD_NO_ERROR;\n\n  HAS_PRELOAD = true;\n  rword entrypoint = getEntrypointAddress();\n\n  int status = qbdipreload_on_start((void *)entrypoint);\n  if (status == QBDIPRELOAD_NOT_HANDLED) {\n    status = qbdipreload_hook_main((void *)entrypoint);\n  }\n  return status;\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/linux_preload.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"QBDIPreload.h\"\n\n#include <dlfcn.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/ucontext.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <QBDI.h>\n\n#if defined(QBDI_ARCH_X86)\n#include \"X86/linux_X86.h\"\n#elif defined(QBDI_ARCH_X86_64)\n#include \"X86_64/linux_X86_64.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"ARM/linux_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"AARCH64/linux_AARCH64.h\"\n#else\n#error \"Architecture not supported\"\n#endif\n\nstatic const size_t STACK_SIZE = 8388608;\nstatic bool HAS_EXITED = false;\nstatic bool HAS_PRELOAD = false;\nstatic bool DEFAULT_HANDLER = false;\nGPRState ENTRY_GPR;\nFPRState ENTRY_FPR;\n\nstruct sigaction default_sa;\nvoid redirectExec(int signum, siginfo_t *info, void *data);\n\nstatic struct {\n  void *address;\n  long value;\n} ENTRY_BRK;\n\nvoid setEntryBreakpoint(void *address_) {\n  long mask, bytecode;\n  void *address = correctAddress(address_, &bytecode, &mask);\n  long pageSize = sysconf(_SC_PAGESIZE);\n  uintptr_t base = (uintptr_t)address - ((uintptr_t)address % pageSize);\n  bool twoPages = ((((uintptr_t)address) + sizeof(long)) > (base + pageSize));\n\n  ENTRY_BRK.address = address;\n  mprotect((void *)base, pageSize, PROT_READ | PROT_WRITE);\n  if (twoPages) {\n    mprotect((void *)(base + pageSize), pageSize, PROT_READ | PROT_WRITE);\n  }\n\n  ENTRY_BRK.value = *((long *)address);\n  *((long *)address) = bytecode | (ENTRY_BRK.value & (~mask));\n\n  mprotect((void *)base, pageSize, PROT_READ | PROT_EXEC);\n  if (twoPages) {\n    mprotect((void *)(base + pageSize), pageSize, PROT_READ | PROT_EXEC);\n  }\n}\n\nvoid unsetEntryBreakpoint() {\n  long pageSize = sysconf(_SC_PAGESIZE);\n  uintptr_t base =\n      (uintptr_t)ENTRY_BRK.address - ((uintptr_t)ENTRY_BRK.address % pageSize);\n  bool twoPages =\n      ((((uintptr_t)ENTRY_BRK.address) + sizeof(long)) > (base + pageSize));\n\n  mprotect((void *)base, pageSize, PROT_READ | PROT_WRITE);\n  if (twoPages) {\n    mprotect((void *)(base + pageSize), pageSize, PROT_READ | PROT_WRITE);\n  }\n\n  *((long *)ENTRY_BRK.address) = ENTRY_BRK.value;\n\n  mprotect((void *)base, pageSize, PROT_READ | PROT_EXEC);\n  if (twoPages) {\n    mprotect((void *)(base + pageSize), pageSize, PROT_READ | PROT_EXEC);\n  }\n\n  struct sigaction sa;\n  if (sigaction(SIGBRK, NULL, &sa) == 0 && (sa.sa_flags & SA_SIGINFO) != 0 &&\n      sa.sa_sigaction == redirectExec) {\n    if (sigaction(SIGBRK, &default_sa, NULL) != 0) {\n      fprintf(stderr, \"Fail to restore sigaction\");\n    }\n  }\n}\n\nvoid catchEntrypoint(int argc, char **argv) {\n  int status = QBDIPRELOAD_NOT_HANDLED;\n\n  unsetEntryBreakpoint();\n\n  status = qbdipreload_on_main(argc, argv);\n\n  if (DEFAULT_HANDLER && (status == QBDIPRELOAD_NOT_HANDLED)) {\n    VMInstanceRef vm;\n    qbdi_initVM(&vm, NULL, NULL, 0);\n    qbdi_instrumentAllExecutableMaps(vm);\n\n    size_t size = 0;\n    qbdi_MemoryMap *modules = qbdi_getCurrentProcessMaps(false, &size);\n\n    // Filter some modules to avoid conflicts\n    qbdi_removeInstrumentedModuleFromAddr(vm, (rword)&catchEntrypoint);\n    removeConflictModule(vm, modules, size);\n\n    qbdi_freeMemoryMapArray(modules, size);\n\n    // Set original states\n    qbdi_setGPRState(vm, &ENTRY_GPR);\n    qbdi_setFPRState(vm, &ENTRY_FPR);\n\n    rword start = QBDI_GPR_GET(qbdi_getGPRState(vm), REG_PC);\n    rword stop = getReturnAddress(qbdi_getGPRState(vm));\n    status = qbdipreload_on_run(vm, start, stop);\n  }\n  exit(status);\n}\n\nvoid redirectExec(int signum, siginfo_t *info, void *data) {\n  ucontext_t *uap = (ucontext_t *)data;\n\n  fix_ucontext_t(uap);\n\n  int status = qbdipreload_on_premain((void *)uap, (void *)uap);\n\n  // Copying the initial thread state\n  qbdipreload_threadCtxToGPRState(uap, &ENTRY_GPR);\n  qbdipreload_floatCtxToFPRState(uap, &ENTRY_FPR);\n\n  // if not handled, use default handler\n  if (status == QBDIPRELOAD_NOT_HANDLED) {\n    DEFAULT_HANDLER = true;\n    void *newStack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,\n                          MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);\n    prepareStack(newStack, STACK_SIZE, uap);\n  }\n\n  setPC(uap, (rword)catchEntrypoint);\n}\n\nstatic void *setupExceptionHandler(void (*action)(int, siginfo_t *, void *)) {\n  struct sigaction sa;\n  sa.sa_sigaction = action;\n  sa.sa_flags = SA_SIGINFO;\n  sigemptyset(&sa.sa_mask);\n  if (sigaction(SIGBRK, &sa, &default_sa) == -1) {\n    fprintf(stderr, \"Could not set redirectExec handler ...\");\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n  return NULL;\n}\n\nvoid *qbdipreload_setup_exception_handler(uint32_t target, uint32_t mask,\n                                          void *handler) {\n  return setupExceptionHandler(handler);\n}\n\nint qbdipreload_hook_main(void *main) {\n  setEntryBreakpoint(main);\n  setupExceptionHandler(redirectExec);\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nQBDI_FORCE_EXPORT void exit(int status) {\n  if (!HAS_EXITED && HAS_PRELOAD) {\n    HAS_EXITED = true;\n    qbdipreload_on_exit(status);\n  }\n  ((void (*)(int))dlsym(RTLD_NEXT, \"exit\"))(status);\n  __builtin_unreachable();\n}\n\nQBDI_FORCE_EXPORT void _exit(int status) {\n  if (!HAS_EXITED && HAS_PRELOAD) {\n    HAS_EXITED = true;\n    qbdipreload_on_exit(status);\n  }\n  ((void (*)(int))dlsym(RTLD_NEXT, \"_exit\"))(status);\n  __builtin_unreachable();\n}\n\ntypedef int (*start_main_fn)(int (*)(int, char **, char **), int, char **,\n                             void (*)(void), void (*)(void), void (*)(void),\n                             void *);\n\nQBDI_FORCE_EXPORT int __libc_start_main(int (*main)(int, char **, char **),\n                                        int argc, char **ubp_av,\n                                        void (*init)(void), void (*fini)(void),\n                                        void (*rtld_fini)(void),\n                                        void(*stack_end)) {\n\n  start_main_fn o_libc_start_main =\n      (start_main_fn)dlsym(RTLD_NEXT, \"__libc_start_main\");\n\n  // do nothing if the library isn't preload\n  if (getenv(\"LD_PRELOAD\") == NULL)\n    return o_libc_start_main(main, argc, ubp_av, init, fini, rtld_fini,\n                             stack_end);\n\n  HAS_PRELOAD = true;\n  int status = qbdipreload_on_start(main);\n  if (status == QBDIPRELOAD_NOT_HANDLED) {\n    status = qbdipreload_hook_main(main);\n  }\n  if (status == QBDIPRELOAD_NO_ERROR) {\n    return o_libc_start_main(main, argc, ubp_av, init, fini, rtld_fini,\n                             stack_end);\n  }\n  exit(0);\n}\n\nint qbdipreload_hook_init() {\n  // not used on linux\n  return QBDIPRELOAD_NO_ERROR;\n}\n"
  },
  {
    "path": "tools/QBDIPreload/src/win_preload.c",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2019 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <Windows.h>\n#include <tlhelp32.h>\n#include <winnt.h>\n#include \"shlwapi.h\"\n#include \"QBDIPreload.h\"\n\n#pragma comment(lib, \"Shlwapi.lib\")\n\n/* Consts */\n#define QBDIPRELOAD_SHARED_MEMORY_NAME_FMT \"qbdi_preload_%u\"\nstatic const unsigned long INST_INT1 =\n    0x01CD; /* Instruction opcode for \"INT 1\"        */\nstatic const unsigned long INST_INT1_MASK = 0xFFFF;\nstatic const size_t QBDI_RUNTIME_STACK_SIZE =\n    0x800000; /* QBDI shadow stack size                */\nstatic const long MEM_PAGE_SIZE = 4096; /* Default page size on Windows */\nstatic const unsigned int SH_MEM_SIZE =\n    4096; /* Shared memory size (attach mode only) */\n\n/* Trampoline spec (to be called from assembly stub) */\nvoid qbdipreload_trampoline();\n\n/* Globals */\nstatic struct {\n  void *va;\n  uint64_t orig_bytes;\n} g_EntryPointInfo; /* Main module EntryPoint (PE from host process) */\nstatic PVOID g_hExceptionHandler; /* VEH for QBDI preload internals (break on\n                                     EntryPoint)                          */\nstatic rword\n    g_firstInstructionVA; /* First instruction that will be executed by QBDI */\nstatic rword\n    g_lastInstructionVA; /* Last instruction that will be executed by QBDI */\nPVOID g_shadowStackTop =\n    NULL; /* QBDI shadow stack top pointer(decreasing address) */\n#if defined(QBDI_ARCH_X86)\nPVOID g_shadowStackBase = NULL; /* QBDI shadow stack base pointer */\n#endif\nstatic GPRState g_EntryPointGPRState; /* QBDI CPU GPR states when EntryPoint has\n                                         been reached */\nstatic FPRState g_EntryPointFPRState; /* QBDI CPU FPR states when EntryPoint has\n                                         been reached */\nstatic HANDLE g_hMainThread; /* Main thread handle (attach mode only) */\nstatic HANDLE g_hShMemMap;   /* Shared memory object between qbdipreload &\n                                external binary (attach mode only) */\nstatic LPVOID g_pShMem; /* Shared memory base pointer (attach mode only)      */\nstatic BOOL g_bIsAttachMode; /* TRUE if attach mode is activated */\n\n/*\n * Conversion from windows CONTEXT ARCH dependent structure\n * to QBDI GPR state (Global purpose registers)\n */\nvoid qbdipreload_threadCtxToGPRState(const void *gprCtx, GPRState *gprState) {\n  PCONTEXT osCpuCtx = (PCONTEXT)gprCtx;\n\n#if defined(QBDI_ARCH_X86_64)\n  gprState->rax = osCpuCtx->Rax;\n  gprState->rbx = osCpuCtx->Rbx;\n  gprState->rcx = osCpuCtx->Rcx;\n  gprState->rdx = osCpuCtx->Rdx;\n  gprState->rsi = osCpuCtx->Rsi;\n  gprState->rdi = osCpuCtx->Rdi;\n  gprState->rbp = osCpuCtx->Rbp;\n  gprState->rsp = osCpuCtx->Rsp;\n  gprState->r8 = osCpuCtx->R8;\n  gprState->r9 = osCpuCtx->R9;\n  gprState->r10 = osCpuCtx->R10;\n  gprState->r11 = osCpuCtx->R11;\n  gprState->r12 = osCpuCtx->R12;\n  gprState->r13 = osCpuCtx->R13;\n  gprState->r14 = osCpuCtx->R14;\n  gprState->r15 = osCpuCtx->R15;\n  gprState->rip = osCpuCtx->Rip;\n#else\n  gprState->eax = osCpuCtx->Eax;\n  gprState->ebx = osCpuCtx->Ebx;\n  gprState->ecx = osCpuCtx->Ecx;\n  gprState->edx = osCpuCtx->Edx;\n  gprState->esi = osCpuCtx->Esi;\n  gprState->edi = osCpuCtx->Edi;\n  gprState->ebp = osCpuCtx->Ebp;\n  gprState->esp = osCpuCtx->Esp;\n  gprState->eip = osCpuCtx->Eip;\n#endif\n  gprState->eflags = osCpuCtx->EFlags;\n}\n\n/* Convert the legacy 2-bit x87 tag word to the FXSAVE abridged format. */\nstatic uint8_t fullTagWordToAbridged(uint16_t fullTag) {\n  uint8_t abridged = 0;\n  for (unsigned i = 0; i < 8; i++) {\n    uint16_t tag = (fullTag >> (i * 2)) & 0x3;\n    if (tag != 0x3) {\n      abridged |= (uint8_t)(1u << i);\n    }\n  }\n  return abridged;\n}\n\n/*\n * Conversion from windows CONTEXT ARCH dependent structure\n * to QBDI FPR state (Floating point registers)\n */\nvoid qbdipreload_floatCtxToFPRState(const void *gprCtx, FPRState *fprState) {\n  PCONTEXT osCpuCtx = (PCONTEXT)gprCtx;\n\n// FPU STmm(X)\n#if defined(QBDI_ARCH_X86_64)\n  memcpy_s(&fprState->stmm0, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[0], sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm1, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[1], sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm2, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[2], sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm3, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[3], sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm4, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[4], sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm5, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[5], sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm6, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[6], sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm7, sizeof(MMSTReg),\n           &osCpuCtx->FltSave.FloatRegisters[7], sizeof(MMSTReg));\n#else\n  memcpy_s(&fprState->stmm0, sizeof(MMSTReg), &osCpuCtx->FloatSave.RegisterArea,\n           sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm1, sizeof(MMSTReg),\n           (LPBYTE)&osCpuCtx->FloatSave.RegisterArea + sizeof(MMSTReg),\n           sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm2, sizeof(MMSTReg),\n           (LPBYTE)&osCpuCtx->FloatSave.RegisterArea + 2 * sizeof(MMSTReg),\n           sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm3, sizeof(MMSTReg),\n           (LPBYTE)&osCpuCtx->FloatSave.RegisterArea + 3 * sizeof(MMSTReg),\n           sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm4, sizeof(MMSTReg),\n           (LPBYTE)&osCpuCtx->FloatSave.RegisterArea + 4 * sizeof(MMSTReg),\n           sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm5, sizeof(MMSTReg),\n           (LPBYTE)&osCpuCtx->FloatSave.RegisterArea + 5 * sizeof(MMSTReg),\n           sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm6, sizeof(MMSTReg),\n           (LPBYTE)&osCpuCtx->FloatSave.RegisterArea + 6 * sizeof(MMSTReg),\n           sizeof(MMSTReg));\n  memcpy_s(&fprState->stmm7, sizeof(MMSTReg),\n           (LPBYTE)&osCpuCtx->FloatSave.RegisterArea + 7 * sizeof(MMSTReg),\n           sizeof(MMSTReg));\n#endif\n\n#if defined(QBDI_ARCH_X86_64)\n  // XMM(X) registers\n  memcpy_s(&fprState->xmm0, 16, &osCpuCtx->Xmm0, 16);\n  memcpy_s(&fprState->xmm1, 16, &osCpuCtx->Xmm1, 16);\n  memcpy_s(&fprState->xmm2, 16, &osCpuCtx->Xmm2, 16);\n  memcpy_s(&fprState->xmm3, 16, &osCpuCtx->Xmm3, 16);\n  memcpy_s(&fprState->xmm4, 16, &osCpuCtx->Xmm4, 16);\n  memcpy_s(&fprState->xmm5, 16, &osCpuCtx->Xmm5, 16);\n  memcpy_s(&fprState->xmm6, 16, &osCpuCtx->Xmm6, 16);\n  memcpy_s(&fprState->xmm7, 16, &osCpuCtx->Xmm7, 16);\n  memcpy_s(&fprState->xmm8, 16, &osCpuCtx->Xmm8, 16);\n  memcpy_s(&fprState->xmm9, 16, &osCpuCtx->Xmm9, 16);\n  memcpy_s(&fprState->xmm10, 16, &osCpuCtx->Xmm10, 16);\n  memcpy_s(&fprState->xmm11, 16, &osCpuCtx->Xmm11, 16);\n  memcpy_s(&fprState->xmm12, 16, &osCpuCtx->Xmm12, 16);\n  memcpy_s(&fprState->xmm13, 16, &osCpuCtx->Xmm13, 16);\n  memcpy_s(&fprState->xmm14, 16, &osCpuCtx->Xmm14, 16);\n  memcpy_s(&fprState->xmm15, 16, &osCpuCtx->Xmm15, 16);\n#endif\n\n// Others FPU registers\n#if defined(QBDI_ARCH_X86_64)\n  fprState->rfcw = osCpuCtx->FltSave.ControlWord;\n  fprState->rfsw = osCpuCtx->FltSave.StatusWord;\n  fprState->ftw = fullTagWordToAbridged(osCpuCtx->FltSave.TagWord);\n  fprState->rsrv1 = osCpuCtx->FltSave.Reserved1;\n  fprState->ip = osCpuCtx->FltSave.ErrorOffset;\n  fprState->cs = osCpuCtx->FltSave.ErrorSelector;\n  fprState->rsrv2 = osCpuCtx->FltSave.Reserved2;\n  fprState->dp = osCpuCtx->FltSave.DataOffset;\n  fprState->ds = osCpuCtx->FltSave.DataSelector;\n  fprState->rsrv3 = osCpuCtx->FltSave.Reserved3;\n  fprState->mxcsr = osCpuCtx->FltSave.MxCsr;\n  fprState->mxcsrmask = osCpuCtx->FltSave.MxCsr_Mask;\n#else\n  fprState->rfcw = osCpuCtx->FloatSave.ControlWord;\n  fprState->rfsw = osCpuCtx->FloatSave.StatusWord;\n  fprState->ftw = fullTagWordToAbridged(osCpuCtx->FloatSave.TagWord);\n  fprState->ip = osCpuCtx->FloatSave.ErrorOffset;\n  fprState->cs = osCpuCtx->FloatSave.ErrorSelector;\n  fprState->dp = osCpuCtx->FloatSave.DataOffset;\n  fprState->ds = osCpuCtx->FloatSave.DataSelector;\n  // Those are not available and set with default QBDI value\n  fprState->mxcsr = 0x1f80;\n  fprState->mxcsrmask = 0xffff;\n#endif\n}\n\n/*\n * Write an \"int 1\" instruction at given address\n * Save previous byte to internal buffer\n * return 0 in case of failure\n */\nint setInt1Exception(void *fn_va) {\n  DWORD oldmemprot;\n\n  if (!fn_va) {\n    return 0;\n  }\n\n  uintptr_t base = (uintptr_t)fn_va - ((uintptr_t)fn_va % MEM_PAGE_SIZE);\n\n  g_EntryPointInfo.va = fn_va;\n  if (!VirtualProtect((void *)base, MEM_PAGE_SIZE, PAGE_READWRITE, &oldmemprot))\n    return 0;\n  g_EntryPointInfo.orig_bytes = *(uint64_t *)fn_va;\n  *(uint64_t *)fn_va =\n      INST_INT1 | (g_EntryPointInfo.orig_bytes & (~(uint64_t)INST_INT1_MASK));\n\n  return VirtualProtect((void *)base, MEM_PAGE_SIZE, oldmemprot, &oldmemprot) !=\n         0;\n}\n\n/*\n * Restore original bytes on a previously installed \"int 1\" instruction\n * return 0 in case of failure\n */\nint unsetInt1Exception() {\n  DWORD oldmemprot;\n  uintptr_t base = (uintptr_t)g_EntryPointInfo.va -\n                   ((uintptr_t)g_EntryPointInfo.va % MEM_PAGE_SIZE);\n\n  if (!VirtualProtect((void *)base, MEM_PAGE_SIZE, PAGE_READWRITE, &oldmemprot))\n    return 0;\n\n  *(uint64_t *)g_EntryPointInfo.va = g_EntryPointInfo.orig_bytes;\n\n  return VirtualProtect((void *)base, MEM_PAGE_SIZE, oldmemprot, &oldmemprot) !=\n         0;\n}\n\n/*\n * Remove a previously installed vectored exception handler\n * Return 0 in case of failure\n */\nint unsetExceptionHandler(LONG (*exception_filter_fn)(PEXCEPTION_POINTERS)) {\n  return RemoveVectoredExceptionHandler(exception_filter_fn);\n}\n\n/*\n * Install a vectored exception handler\n * Return 0 in case of failure\n */\nint setExceptionHandler(PVECTORED_EXCEPTION_HANDLER exception_filter_fn) {\n\n  g_hExceptionHandler = AddVectoredExceptionHandler(1, exception_filter_fn);\n  return g_hExceptionHandler != NULL;\n}\n\n/*\n * Remove some common window modules\n */\nvoid removeConflictModule(VMInstanceRef vm, qbdi_MemoryMap *modules,\n                          size_t size) {\n  size_t i;\n  for (i = 0; i < size; i++) {\n    if ((modules[i].permission & QBDI_PF_EXEC) &&\n        (StrStrIA(modules[i].name, \"advapi\") ||\n         StrStrIA(modules[i].name, \"combase\") ||\n         StrStrIA(modules[i].name, \"comctl32\") ||\n         StrStrIA(modules[i].name, \"comdlg\") ||\n         StrStrIA(modules[i].name, \"gdi32\") ||\n         StrStrIA(modules[i].name, \"gdiplus\") ||\n         StrStrIA(modules[i].name, \"imm32\") ||\n         StrStrIA(modules[i].name, \"kernel\") ||\n         StrStrIA(modules[i].name, \"msvcp\") ||\n         StrStrIA(modules[i].name, \"msvcrt\") ||\n         StrStrIA(modules[i].name, \"ntdll\") ||\n         StrStrIA(modules[i].name, \"ole32\") ||\n         StrStrIA(modules[i].name, \"oleaut\") ||\n         StrStrIA(modules[i].name, \"rpcrt\") ||\n         StrStrIA(modules[i].name, \"sechost\") ||\n         StrStrIA(modules[i].name, \"shcore\") ||\n         StrStrIA(modules[i].name, \"shell32\") ||\n         StrStrIA(modules[i].name, \"shlwapi\") ||\n         StrStrIA(modules[i].name, \"ucrtbase\") ||\n         StrStrIA(modules[i].name, \"user32\") ||\n         StrStrIA(modules[i].name, \"uxtheme\") ||\n         StrStrIA(modules[i].name, \"vcruntime\") ||\n         StrStrIA(modules[i].name, \"win32u\") || modules[i].name[0] == 0)) {\n      qbdi_removeInstrumentedRange(vm, modules[i].start, modules[i].end);\n    }\n  }\n}\n\n/*\n * Trampoline implementation\n * It removes exception handler, restore entry point bytes and\n * setup QBDI runtime for host target before calling user callback \"on_run\"\n * Its is called from separate qbdipreload_trampoline() assembly stub that\n * makes this function load in a arbitraty allocated stack, then QBDI can\n * safely initialize & instrument main target thread\n */\nvoid qbdipreload_trampoline_impl() {\n  unsetInt1Exception();\n  unsetExceptionHandler(g_hExceptionHandler);\n\n  // On windows only entry point call is catched\n  // but not main function\n  int status = qbdipreload_on_main(0, NULL);\n\n  if (status == QBDIPRELOAD_NOT_HANDLED) {\n    VMInstanceRef vm;\n    qbdi_initVM(&vm, NULL, NULL, 0);\n    qbdi_instrumentAllExecutableMaps(vm);\n\n    size_t size = 0;\n    qbdi_MemoryMap *modules = qbdi_getCurrentProcessMaps(false, &size);\n\n    // Filter some modules to avoid conflicts\n    qbdi_removeInstrumentedModuleFromAddr(vm,\n                                          (rword)&qbdipreload_trampoline_impl);\n    removeConflictModule(vm, modules, size);\n\n    qbdi_freeMemoryMapArray(modules, size);\n\n    // Set original CPU state\n    qbdi_setGPRState(vm, &g_EntryPointGPRState);\n    qbdi_setFPRState(vm, &g_EntryPointFPRState);\n\n    // User final callback call as QBDIPreload is ready\n    status = qbdipreload_on_run(vm, g_firstInstructionVA, g_lastInstructionVA);\n  }\n\n  // Exiting early must be done as qbdipreload_trampoline_impl\n  // is executed on fake stack without any caller\n  // It will trigger DLL_PROCESS_DETACH event in DLLMain()\n  ExitProcess(status);\n}\n\n/*\n * Windows QBDI preload specific excetion handler\n * It must be uninstalled once used one time\n * The handler catches the first INT1 exception\n */\nLONG WINAPI QbdiPreloadExceptionFilter(PEXCEPTION_POINTERS exc_info) {\n  PCONTEXT osCpuCtx = exc_info->ContextRecord;\n\n  // Sanity check on exception\n  if ((exc_info->ExceptionRecord->ExceptionCode ==\n       EXCEPTION_ACCESS_VIOLATION) &&\n#if defined(QBDI_ARCH_X86_64)\n      (osCpuCtx->Rip == (DWORD64)g_EntryPointInfo.va)) {\n#else\n      (osCpuCtx->Eip == (DWORD)g_EntryPointInfo.va)) {\n#endif\n    // Call user provided callback with x64 cpu state (specific to windows)\n    int status = qbdipreload_on_premain((void *)osCpuCtx, (void *)osCpuCtx);\n\n    // Convert windows CPU context to QBDIGPR/FPR states\n    qbdipreload_threadCtxToGPRState(osCpuCtx, &g_EntryPointGPRState);\n    qbdipreload_floatCtxToFPRState(osCpuCtx, &g_EntryPointFPRState);\n\n    // First instruction to execute is main module entry point)\n    g_firstInstructionVA = QBDI_GPR_GET(&g_EntryPointGPRState, REG_PC);\n\n    // In case if attach mode, it's difficult to guess on which instruction\n    // stopping the instruction\n    if (g_bIsAttachMode) {\n      g_lastInstructionVA = (rword)0xFFFFFFFFFFFFFFFF;\n    }\n    // If start function has been hooked\n    // Last instruction to execute is inside windows PE loader\n    // (inside BaseThreadInitThunk() who called PE entry point & set RIP on\n    // stack)\n    else {\n      g_lastInstructionVA =\n          *((rword *)QBDI_GPR_GET(&g_EntryPointGPRState, REG_SP));\n    }\n\n    if (status == QBDIPRELOAD_NOT_HANDLED) {\n      // Allocate shadow stack & keep some space at the end for QBDI runtime\n      g_shadowStackTop = VirtualAlloc(NULL, QBDI_RUNTIME_STACK_SIZE,\n                                      MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);\n      g_shadowStackTop = (PVOID)((uint64_t)g_shadowStackTop +\n                                 QBDI_RUNTIME_STACK_SIZE - 0x1008);\n#if defined(QBDI_ARCH_X86)\n      g_shadowStackBase = g_shadowStackTop;\n#endif\n    }\n\n// Continue execution on trampoline to make QBDI runtime\n// execute using a separate stack and not instrumented target one\n// RSP can't be set here (system seems to validate pointer authenticity)\n#if defined(QBDI_ARCH_X86_64)\n    osCpuCtx->Rip = (uint64_t)qbdipreload_trampoline;\n#else\n    osCpuCtx->Eip = (uint32_t)qbdipreload_trampoline;\n#endif\n  }\n\n  return EXCEPTION_CONTINUE_EXECUTION;\n}\n\n/*\n * Get main module entry point\n */\nvoid *getMainModuleEntryPoint() {\n  PIMAGE_DOS_HEADER mainmod_imgbase = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);\n  PIMAGE_NT_HEADERS mainmod_nt =\n      (PIMAGE_NT_HEADERS)((uint8_t *)mainmod_imgbase +\n                          mainmod_imgbase->e_lfanew);\n\n  return (void *)((uint8_t *)mainmod_imgbase +\n                  mainmod_nt->OptionalHeader.AddressOfEntryPoint);\n}\n\n/*\n * Enable DEBUG privilege for current process\n * Return 0 in case of error, 1 otherwise\n */\nint enable_debug_privilege() {\n  HANDLE hToken;\n  LUID SeDebugNameValue;\n  TOKEN_PRIVILEGES TokenPrivileges;\n  int result = 0;\n\n  if (OpenProcessToken(GetCurrentProcess(),\n                       TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {\n    if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &SeDebugNameValue)) {\n      TokenPrivileges.PrivilegeCount = 1;\n      TokenPrivileges.Privileges[0].Luid = SeDebugNameValue;\n      TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n\n      if (AdjustTokenPrivileges(hToken, FALSE, &TokenPrivileges,\n                                sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {\n        result = 1;\n      }\n    }\n    CloseHandle(hToken);\n  }\n\n  return result;\n}\n\n/*\n * Return thread RIP register from main module\n * Must be called when the main thread is in \"suspended\" state\n */\n#ifndef MAKEULONGLONG\n#define MAKEULONGLONG(ldw, hdw) (((ULONGLONG)hdw << 32) | ((ldw) & 0xFFFFFFFF))\n#endif\n\n#ifndef MAXULONGLONG\n#define MAXULONGLONG ((ULONGLONG) ~((ULONGLONG)0))\n#endif\n\nvoid *getMainThreadRip() {\n  CONTEXT osCpuCtx = {0};\n  DWORD dwMainThreadID = 0, dwProcID = GetCurrentProcessId();\n  ULONGLONG ullMinCreateTime = MAXULONGLONG;\n  THREADENTRY32 th32;\n  FILETIME afTimes[4] = {0};\n  HANDLE hThread;\n  void *result = NULL;\n\n  // Loop through current process threads\n  HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);\n\n  if (hThreadSnap == INVALID_HANDLE_VALUE)\n    return NULL;\n\n  th32.dwSize = sizeof(THREADENTRY32);\n  BOOL bOK = TRUE;\n  for (bOK = Thread32First(hThreadSnap, &th32); bOK;\n       bOK = Thread32Next(hThreadSnap, &th32)) {\n    if (th32.th32OwnerProcessID == dwProcID) {\n      hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, th32.th32ThreadID);\n      if (hThread) {\n        if (GetThreadTimes(hThread, &afTimes[0], &afTimes[1], &afTimes[2],\n                           &afTimes[3])) {\n          ULONGLONG ullTest = MAKEULONGLONG(afTimes[0].dwLowDateTime,\n                                            afTimes[0].dwHighDateTime);\n          if (ullTest && ullTest < ullMinCreateTime) {\n            ullMinCreateTime = ullTest;\n            dwMainThreadID = th32.th32ThreadID; // Main thread should be the\n                                                // oldest created one\n          }\n        }\n        CloseHandle(hThread);\n      }\n    }\n  }\n\n  CloseHandle(hThreadSnap);\n\n  // Enable debug priviliges for current process, open target main thread\n  // and grab CPU context (it is reliable/frozen as the thread should be in\n  // suspended state)\n  if (dwMainThreadID && enable_debug_privilege()) {\n    g_hMainThread = OpenThread(THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT |\n                                   THREAD_SUSPEND_RESUME,\n                               FALSE, dwMainThreadID);\n\n    if (g_hMainThread) {\n      osCpuCtx.ContextFlags = CONTEXT_ALL;\n      if (GetThreadContext(g_hMainThread, &osCpuCtx)) {\n#if defined(QBDI_ARCH_X86_64)\n        result = (void *)osCpuCtx.Rip;\n#else\n        result = (void *)osCpuCtx.Eip;\n#endif\n      }\n    }\n  }\n\n  return result;\n}\n\n/*\n * Hooking based on int1 instruction + exception handler\n * Return 0 in case of failure\n */\nint qbdipreload_hook(void *va) {\n  if (!setInt1Exception(va)) {\n    return 0;\n  }\n\n  return !setExceptionHandler(QbdiPreloadExceptionFilter);\n}\n\n/*\n * Attach mode initialization\n * Shared memory is initialized to make the QBDIPreload\n * able to access data from host injector\n */\nBOOLEAN qbdipreload_attach_init() {\n  TCHAR szShMemName[32];\n  BOOL result = FALSE;\n\n  snprintf(szShMemName, sizeof(szShMemName), QBDIPRELOAD_SHARED_MEMORY_NAME_FMT,\n           GetCurrentProcessId());\n  g_hShMemMap =\n      OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, szShMemName);\n  g_pShMem = NULL;\n\n  if (g_hShMemMap) {\n    g_bIsAttachMode = TRUE;\n\n    g_pShMem =\n        MapViewOfFile(g_hShMemMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);\n    if (g_pShMem) {\n      result = TRUE;\n    }\n  } else {\n    g_bIsAttachMode = FALSE;\n  }\n  return result;\n}\n\n/*\n * Read data from shared memory\n */\nBOOLEAN qbdipreload_read_shmem(LPVOID lpData, DWORD dwMaxBytesRead) {\n\n  if (lpData && dwMaxBytesRead && g_pShMem && (dwMaxBytesRead <= SH_MEM_SIZE)) {\n    memcpy_s(lpData, dwMaxBytesRead, g_pShMem, dwMaxBytesRead);\n    return TRUE;\n  }\n\n  return FALSE;\n}\n\n/*\n * Attach mode closing\n */\nvoid qbdipreload_attach_close() {\n  if (g_pShMem) {\n    UnmapViewOfFile(g_pShMem);\n    g_pShMem = NULL;\n  }\n\n  if (g_hShMemMap) {\n    CloseHandle(g_hShMemMap);\n    g_hShMemMap = NULL;\n  }\n}\n\n/*\n * QBDI windows preload installation is done automatically\n * through DLLMain() once the QBDI instrumentation module\n * is loaded inside target (e.g. with LoadLibrary)\n * This function is automatically called when user use\n * QBDIPRELOAD_INIT macro\n */\nBOOLEAN qbdipreload_hook_init(DWORD nReason) {\n  if (nReason == DLL_PROCESS_ATTACH) {\n    void *hook_target_va;\n\n    if (g_bIsAttachMode) {\n      hook_target_va = getMainThreadRip();\n    } else {\n      hook_target_va = getMainModuleEntryPoint();\n    }\n    // Call user provided callback on start\n    int status = qbdipreload_on_start(hook_target_va);\n    if (status == QBDIPRELOAD_NOT_HANDLED) {\n      // QBDI preload installation\n      qbdipreload_hook(hook_target_va);\n      if (g_bIsAttachMode && g_hMainThread) {\n        ResumeThread(g_hMainThread);\n      }\n    }\n  }\n  // Call user provided exit callback on DLL unloading\n  else if (nReason == DLL_PROCESS_DETACH) {\n    DWORD dwExitCode;\n    GetExitCodeProcess(GetCurrentProcess(), &dwExitCode);\n    qbdipreload_on_exit(dwExitCode);\n    if (g_bIsAttachMode) {\n      qbdipreload_attach_close();\n    }\n  }\n\n  return TRUE;\n}\n"
  },
  {
    "path": "tools/QBDIWinPreloader/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\nproject(QBDIWinPreloader)\n\nadd_executable(QBDIWinPreloader)\ntarget_sources(QBDIWinPreloader\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/src/QBDIWinPreloader.c\")\n\ninstall(\n  TARGETS QBDIWinPreloader\n  EXPORT QBDIWinPreloader_targets\n  RUNTIME DESTINATION bin)\n\nif(PYQBDI_OUTPUT_DIRECTORY AND QBDI_TOOLS_PYQBDI)\n  # If we build PyQBDI in order to create a wheel, we copy the binary\n  # to the output directory in order to include it in the wheel.\n  add_custom_command(\n    TARGET QBDIWinPreloader\n    POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:QBDIWinPreloader>\n            \"${PYQBDI_OUTPUT_DIRECTORY}/pyqbdiWinPreloader.exe\")\nendif()\n"
  },
  {
    "path": "tools/QBDIWinPreloader/src/QBDIWinPreloader.c",
    "content": "#include \"stdio.h\"\n#include \"string.h\"\n#include \"windows.h\"\n\nint main(int argc, TCHAR **argv) {\n  STARTUPINFOW si = {0};\n  PROCESS_INFORMATION pi = {0};\n  int argCount;\n  si.cb = sizeof(si);\n\n  LPWSTR commandLine = GetCommandLineW();\n  LPWSTR *argList = CommandLineToArgvW(commandLine, &argCount);\n  if (!argList) {\n    printf(\"Failed to retrieve commandline arguments\\n\");\n    return -1;\n  }\n\n  printf(\"QBDI Windows Preloader Tool\\n\");\n  printf(\"---------------------------\\n\\n\");\n\n  if (argCount < 3) {\n    printf(\n        \"Usage: QBDIWinPreloader <library> <executable> [<parameters> \"\n        \"...]\\n\");\n    LocalFree(argList);\n    return -1;\n  }\n\n  wchar_t library[MAX_PATH] = {0};\n  wcscpy_s(library, MAX_PATH, argList[1]);\n\n  wchar_t target[MAX_PATH] = {0};\n  wcscpy_s(target, MAX_PATH, argList[2]);\n\n  // Create a sage escaped command line is really difficult. However,\n  // we alreay have GetCommandLineW() that is escaped with 2 additionnals\n  // arguments at the begin. Just remove these two args to have the target\n  // escape command line.\n\n  LPWSTR targetCmdLineRO = commandLine;\n  for (int i = 0; i < 2; i++) {\n    LPWSTR currentArg = argList[i];\n    while (currentArg[0] != L'\\0') {\n      while (targetCmdLineRO[0] != currentArg[0]) {\n        if (targetCmdLineRO[0] == L'\\0') {\n          printf(\"Failed to create commandLine for target : %ls\\n\",\n                 commandLine);\n          return -1;\n        }\n        targetCmdLineRO++;\n      }\n      currentArg++;\n    }\n    while (targetCmdLineRO[0] != L' ') {\n      if (targetCmdLineRO[0] == L'\\0') {\n        printf(\"Failed to create commandLine for target : %ls\\n\", commandLine);\n        return -1;\n      }\n      targetCmdLineRO++;\n    }\n    while (targetCmdLineRO[0] == L' ') {\n      if (targetCmdLineRO[0] == L'\\0') {\n        printf(\"Failed to create commandLine for target : %ls\\n\", commandLine);\n        return -1;\n      }\n      targetCmdLineRO++;\n    }\n  }\n\n  wchar_t *targetCmdLine =\n      malloc(sizeof(wchar_t) * wcslen(targetCmdLineRO) + 1);\n  wcscpy(targetCmdLine, targetCmdLineRO);\n\n  printf(\"Target: %ls\\n\", target);\n  printf(\"Target CommandLine: %ls\\n\", targetCmdLine);\n  printf(\"Library: %ls\\n\", library);\n\n  if (GetFileAttributesW(library) == INVALID_FILE_ATTRIBUTES) {\n    printf(\"Failed to find library(%ls)\\n\", library);\n    return -1;\n  }\n\n  BOOL cRes = CreateProcessW(target, targetCmdLine, NULL, NULL, TRUE,\n                             CREATE_SUSPENDED, NULL, NULL, &si, &pi);\n  if (!cRes) {\n    printf(\"Process start failed(%ld)\\n\", GetLastError());\n    return -1;\n  }\n\n  LPVOID procLoadLibrary =\n      (LPVOID)GetProcAddress(GetModuleHandleW(L\"kernel32.dll\"), \"LoadLibraryW\");\n  LPVOID memLibStr = (LPVOID)VirtualAllocEx(\n      pi.hProcess, NULL, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);\n  if (memLibStr == NULL) {\n    printf(\"Failed to allocate memory in process(%ld)\\n\", GetLastError());\n    TerminateProcess(pi.hProcess, -1);\n    return -1;\n  }\n\n  if (WriteProcessMemory(pi.hProcess, memLibStr, (wchar_t *)library, MAX_PATH,\n                         NULL) == 0) {\n    printf(\"Failed to write process memory(%ld)\\n\", GetLastError());\n    TerminateProcess(pi.hProcess, -1);\n    return -1;\n  }\n\n  printf(\"Launching Library Main\\n\");\n  HANDLE remoteThread = CreateRemoteThread(\n      pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)procLoadLibrary, memLibStr,\n      0, NULL);\n  if (remoteThread == 0) {\n    printf(\"Failed to create remote process thread(%ld)\\n\", GetLastError());\n    TerminateProcess(pi.hProcess, -1);\n    return -1;\n  }\n\n  if (WaitForSingleObject(remoteThread, INFINITE) == WAIT_FAILED) {\n    printf(\"Wait for remote thread failed(%ld)\\n\", GetLastError());\n    return 1;\n  }\n\n  printf(\"Library Main Finished\\n\");\n  printf(\"Resuming Process\\n\");\n  ResumeThread(pi.hThread);\n\n  if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) {\n    printf(\"Wait for main thread failed(%ld)\\n\", GetLastError());\n    return 1;\n  }\n\n  CloseHandle(pi.hThread);\n  CloseHandle(pi.hProcess);\n  LocalFree(argList);\n  free(targetCmdLine);\n  return 0;\n}\n"
  },
  {
    "path": "tools/frida-qbdi.js",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n/*\n * Usage:\n * $ frida -n Twitter -l frida-qbdi.js\n *\n */\nexport var QBDI_MAJOR = 0;\nexport var QBDI_MINOR = 12;\nexport var QBDI_PATCH = 1;\n/**\n * Minimum version of QBDI to use Frida bindings\n */\nexport var QBDI_MINIMUM_VERSION = (QBDI_MAJOR << 16) | (QBDI_MINOR << 8) | QBDI_PATCH;\n\nif (typeof Duktape === 'object') {\n    // Warn about duktape runtime (except on iOS...)\n    if (Process.platform !== 'darwin' || Process.arch.indexOf(\"arm\") !== 0) {\n        console.warn(\"[!] Warning: using duktape runtime is much slower...\");\n        console.warn(\"    => Frida --enable-jit option should be used\");\n    }\n}\n\n// Provide a generic and \"safe\" (no exceptions if symbol is not found) way to load\n// a library and bind/create a native function\nclass Binder {\n    constructor() { }\n\n    findLibrary(lib, paths) {\n        if (lib === undefined) {\n            return undefined;\n        }\n        var cpath = undefined;\n        if (paths !== undefined) {\n            var cnt = paths.length;\n            var found = false;\n            // try to find our library\n            for (var i = 0; i < cnt; i++) {\n                cpath = paths[i] + lib;\n                // use Frida file interface to test if file exists...\n                try {\n                    var fp = new File(cpath, \"rb\");\n                    fp.close();\n                    found = true;\n                    break;\n                } catch (e) {\n                    continue;\n                }\n            }\n            if (!found) {\n                return undefined;\n            }\n        } else {\n            cpath = lib;\n        }\n        return cpath;\n    }\n\n    safeNativeFunction(cbk, ret, args) {\n        var e = cbk();\n        if (!e) {\n            return undefined;\n        }\n        return new NativeFunction(e, ret, args);\n    }\n\n    load(lib, paths) {\n        var cpath = this.findLibrary(lib, paths);\n        if (cpath === undefined) {\n            var errmsg = lib + ' library not found...';\n            console.error(errmsg);\n            throw new Error(errmsg);\n        }\n        // load library\n        var handle = System.dlopen(cpath);\n        if (handle.isNull()) {\n            var errmsg = 'Failed to load ' + cpath + ' (' + System.dlerror() + ')';\n            console.error(errmsg);\n            throw new Error(errmsg);\n        }\n        return cpath;\n    }\n\n    bind(name, ret, args) {\n        return this.safeNativeFunction(function () {\n            return Module.findGlobalExportByName(name);\n        }, ret, args);\n    }\n}\n\n\nclass QBDIBinder extends Binder {\n    /**\n     * QBDI library name\n     */\n    get QBDI_LIB() {\n        return {\n            'linux': 'libQBDI.so',\n            'darwin': 'libQBDI.dylib',\n            'windows': 'QBDI.dll',\n        }[Process.platform];\n    }\n\n    // paths where QBDI library may be\n    get QBDI_PATHS() {\n        return [\n            //palera1n path\n            '/var/jb/usr/lib',\n            // UNIX default paths\n            '/usr/lib/',\n            '/usr/local/lib/',\n            // advised Android path\n            '/data/local/tmp/',\n            // in case of a local archive\n            './',\n            './lib',\n            // Windows default path\n            'C:\\\\Program Files\\\\QBDI ' + QBDI_MAJOR + '.' + QBDI_MINOR + '.' + QBDI_PATCH + '\\\\lib\\\\'\n        ];\n    }\n\n    bind(name, ret, args) {\n        var libpath = this.QBDI_LIB;\n        return this.safeNativeFunction(function () {\n            return Process.getModuleByName(libpath).findExportByName(name);\n        }, ret, args);\n    }\n\n    load() {\n        return super.load(this.QBDI_LIB, this.QBDI_PATHS);\n    }\n}\n\n\nvar _binder = new Binder();\nvar _qbdibinder = new QBDIBinder();\n\n\n// Needed to load QBDI\nvar System_C = Object.freeze({\n    LoadLibraryEx: _binder.bind('LoadLibraryExA', 'pointer', ['pointer', 'int', 'int']),\n    GetLastError: _binder.bind('GetLastError', 'int', []),\n    dlopen: _binder.bind('dlopen', 'pointer', ['pointer', 'int']),\n    dlerror: _binder.bind('dlerror', 'pointer', []),\n    free: _binder.bind('free', 'void', ['pointer']),\n});\n\n\nvar System = Object.freeze({\n    dlerror: function () {\n        if (Process.platform === \"windows\") {\n            var val = System_C.GetLastError();\n            if (val === undefined) {\n                return undefined;\n            }\n            return val.toString();\n        }\n        var strPtr = System_C.dlerror();\n        return strPtr.readCString();\n\n    },\n    dlopen: function (library) {\n        var RTLD_LOCAL = 0x0;\n        var RTLD_LAZY = 0x1;\n        var path = Memory.allocUtf8String(library);\n        if (Process.platform === \"windows\") {\n            return System_C.LoadLibraryEx(path, 0, 0);\n        }\n        return System_C.dlopen(path, RTLD_LOCAL | RTLD_LAZY);\n    },\n    free: function (ptr) {\n        System_C.free(ptr);\n    }\n});\n\n/**\n * Fullpath of the QBDI library\n */\n// Load QBDI library\nvar QBDI_LIB_FULLPATH = _qbdibinder.load();\n\n// Define rword type and interfaces\n\n/**\n * An alias to Frida uint type with the size of general registers (**uint64** or **uint32**)\n */\nexport var rword = Process.pointerSize === 8 ? 'uint64' : 'uint32';\nexport var sword = Process.pointerSize === 8 ? 'int64' : 'int32';\n\nNativePointer.prototype.readRword = Process.pointerSize === 8 ? NativePointer.prototype.readU64 : NativePointer.prototype.readU32;\nNativePointer.prototype.readSword = Process.pointerSize === 8 ? NativePointer.prototype.readS64 : NativePointer.prototype.readS32;\n\nNativePointer.prototype.writeRword = Process.pointerSize === 8 ? NativePointer.prototype.writeU64 : NativePointer.prototype.writeU32;\nNativePointer.prototype.writeSword = Process.pointerSize === 8 ? NativePointer.prototype.writeS64 : NativePointer.prototype.writeS32;\n\n// Convert a number to its register-sized representation\n\n/**\n * Convert a NativePointer into a type with the size of a register (``Number`` or ``UInt64``).\n */\nNativePointer.prototype.toRword = function () {\n    // Nothing better really ?\n    if (Process.pointerSize === 8) {\n        return uint64(\"0x\" + this.toString(16));\n    }\n    return parseInt(this.toString(16), 16);\n}\n\n/**\n * Convert a number into a type with the size of a register (``Number`` or ``UInt64``).\n * Can't be used for numbers > 32 bits, would cause weird results due to IEEE-754.\n */\nNumber.prototype.toRword = function () {\n    if (this > 0x100000000) {\n        throw new TypeError('For integer > 32 bits, please use Frida uint64 type.');\n    }\n    if (Process.pointerSize === 8) {\n        return uint64(this);\n    }\n    return this;\n}\n\n/**\n * An identity function (returning the same ``UInt64`` object).\n * It exists only to provide a unified **toRword** interface.\n */\nUInt64.prototype.toRword = function () {\n    return this;\n}\n\nInt64.prototype.toSword = function () {\n    return this;\n}\n\n// Some helpers\n\nString.prototype.leftPad = function (paddingValue, paddingLength) {\n    paddingLength = paddingLength || paddingValue.length;\n    if (paddingLength < this.length) {\n        return String(this);\n    }\n    return String(paddingValue + this).slice(-paddingLength);\n};\n\n/**\n * Convert a String into a type with the size of a register (``Number`` or ``UInt64``).\n */\nString.prototype.toRword = function () {\n    return ptr(this).toRword()\n};\n\n/**\n * This function is used to pretty print a pointer, padded with 0 to the size of a register.\n *\n * @param ptr Pointer you want to pad\n *\n * @return pointer value as padded string (ex: \"0x00004242\")\n */\nexport function hexPointer(ptr) {\n    return ptr.toString(16).leftPad(\"0000000000000000\", Process.pointerSize * 2);\n}\n\n\n//\nvar QBDI_C = Object.freeze({\n    // VM\n    initVM: _qbdibinder.bind('qbdi_initVM', 'void', ['pointer', 'pointer', 'pointer', rword]),\n    terminateVM: _qbdibinder.bind('qbdi_terminateVM', 'void', ['pointer']),\n    getOptions: _qbdibinder.bind('qbdi_getOptions', rword, ['pointer']),\n    setOptions: _qbdibinder.bind('qbdi_setOptions', 'void', ['pointer', rword]),\n    addInstrumentedRange: _qbdibinder.bind('qbdi_addInstrumentedRange', 'void', ['pointer', rword, rword]),\n    addInstrumentedModule: _qbdibinder.bind('qbdi_addInstrumentedModule', 'uchar', ['pointer', 'pointer']),\n    addInstrumentedModuleFromAddr: _qbdibinder.bind('qbdi_addInstrumentedModuleFromAddr', 'uchar', ['pointer', rword]),\n    instrumentAllExecutableMaps: _qbdibinder.bind('qbdi_instrumentAllExecutableMaps', 'uchar', ['pointer']),\n    removeInstrumentedRange: _qbdibinder.bind('qbdi_removeInstrumentedRange', 'void', ['pointer', rword, rword]),\n    removeInstrumentedModule: _qbdibinder.bind('qbdi_removeInstrumentedModule', 'uchar', ['pointer', 'pointer']),\n    removeInstrumentedModuleFromAddr: _qbdibinder.bind('qbdi_removeInstrumentedModuleFromAddr', 'uchar', ['pointer', rword]),\n    removeAllInstrumentedRanges: _qbdibinder.bind('qbdi_removeAllInstrumentedRanges', 'void', ['pointer']),\n    run: _qbdibinder.bind('qbdi_run', 'uchar', ['pointer', rword, rword]),\n    call: _qbdibinder.bind('qbdi_call', 'uchar', ['pointer', 'pointer', rword, 'uint32',\n        '...', rword, rword, rword, rword, rword, rword, rword, rword, rword, rword]),\n    switchStackAndCall: _qbdibinder.bind('qbdi_switchStackAndCall', 'uchar', ['pointer', 'pointer', rword, 'uint32', 'uint32',\n        '...', rword, rword, rword, rword, rword, rword, rword, rword, rword, rword]),\n    getGPRState: _qbdibinder.bind('qbdi_getGPRState', 'pointer', ['pointer']),\n    getFPRState: _qbdibinder.bind('qbdi_getFPRState', 'pointer', ['pointer']),\n    getErrno: _qbdibinder.bind('qbdi_getErrno', 'uint32', ['pointer']),\n    setGPRState: _qbdibinder.bind('qbdi_setGPRState', 'void', ['pointer', 'pointer']),\n    setFPRState: _qbdibinder.bind('qbdi_setFPRState', 'void', ['pointer', 'pointer']),\n    setErrno: _qbdibinder.bind('qbdi_setErrno', 'void', ['pointer', 'uint32']),\n    addMnemonicCB: _qbdibinder.bind('qbdi_addMnemonicCB', 'uint32', ['pointer', 'pointer', 'uint32', 'pointer', 'pointer', 'int32']),\n    addMemAccessCB: _qbdibinder.bind('qbdi_addMemAccessCB', 'uint32', ['pointer', 'uint32', 'pointer', 'pointer', 'int32']),\n    addInstrRule: _qbdibinder.bind('qbdi_addInstrRule', 'uint32', ['pointer', 'pointer', 'uint32', 'pointer']),\n    addInstrRuleRange: _qbdibinder.bind('qbdi_addInstrRuleRange', 'uint32', ['pointer', rword, rword, 'pointer', 'uint32', 'pointer']),\n    addInstrRuleData: _qbdibinder.bind('qbdi_addInstrRuleData', 'void', ['pointer', 'uint32', 'pointer', 'pointer', 'int32']),\n    addMemAddrCB: _qbdibinder.bind('qbdi_addMemAddrCB', 'uint32', ['pointer', rword, 'uint32', 'pointer', 'pointer']),\n    addMemRangeCB: _qbdibinder.bind('qbdi_addMemRangeCB', 'uint32', ['pointer', rword, rword, 'uint32', 'pointer', 'pointer']),\n    addCodeCB: _qbdibinder.bind('qbdi_addCodeCB', 'uint32', ['pointer', 'uint32', 'pointer', 'pointer', 'int32']),\n    addCodeAddrCB: _qbdibinder.bind('qbdi_addCodeAddrCB', 'uint32', ['pointer', rword, 'uint32', 'pointer', 'pointer', 'int32']),\n    addCodeRangeCB: _qbdibinder.bind('qbdi_addCodeRangeCB', 'uint32', ['pointer', rword, rword, 'uint32', 'pointer', 'pointer', 'int32']),\n    addVMEventCB: _qbdibinder.bind('qbdi_addVMEventCB', 'uint32', ['pointer', 'uint32', 'pointer', 'pointer']),\n    deleteInstrumentation: _qbdibinder.bind('qbdi_deleteInstrumentation', 'uchar', ['pointer', 'uint32']),\n    deleteAllInstrumentations: _qbdibinder.bind('qbdi_deleteAllInstrumentations', 'void', ['pointer']),\n    getInstAnalysis: _qbdibinder.bind('qbdi_getInstAnalysis', 'pointer', ['pointer', 'uint32']),\n    getCachedInstAnalysis: _qbdibinder.bind('qbdi_getCachedInstAnalysis', 'pointer', ['pointer', rword, 'uint32']),\n    getJITInstAnalysis: _qbdibinder.bind('qbdi_getJITInstAnalysis', 'pointer', ['pointer', rword, 'uint32']),\n    recordMemoryAccess: _qbdibinder.bind('qbdi_recordMemoryAccess', 'uchar', ['pointer', 'uint32']),\n    getInstMemoryAccess: _qbdibinder.bind('qbdi_getInstMemoryAccess', 'pointer', ['pointer', 'pointer']),\n    getBBMemoryAccess: _qbdibinder.bind('qbdi_getBBMemoryAccess', 'pointer', ['pointer', 'pointer']),\n    // Memory\n    allocateVirtualStack: _qbdibinder.bind('qbdi_allocateVirtualStack', 'uchar', ['pointer', 'uint32', 'pointer']),\n    alignedAlloc: _qbdibinder.bind('qbdi_alignedAlloc', 'pointer', ['uint32', 'uint32']),\n    alignedFree: _qbdibinder.bind('qbdi_alignedFree', 'void', ['pointer']),\n    simulateCall: _qbdibinder.bind('qbdi_simulateCall', 'void', ['pointer', rword, 'uint32',\n        '...', rword, rword, rword, rword, rword, rword, rword, rword, rword, rword]),\n    getModuleNames: _qbdibinder.bind('qbdi_getModuleNames', 'pointer', ['pointer']),\n    // Logs\n    setLogPriority: _qbdibinder.bind('qbdi_setLogPriority', 'void', ['uint32']),\n    // Helpers\n    getVersion: _qbdibinder.bind('qbdi_getVersion', 'pointer', ['pointer']),\n    getGPR: _qbdibinder.bind('qbdi_getGPR', rword, ['pointer', 'uint32']),\n    setGPR: _qbdibinder.bind('qbdi_setGPR', 'void', ['pointer', 'uint32', rword]),\n    getMemoryAccessStructDesc: _qbdibinder.bind('qbdi_getMemoryAccessStructDesc', 'pointer', []),\n    getVMStateStructDesc: _qbdibinder.bind('qbdi_getVMStateStructDesc', 'pointer', []),\n    getOperandAnalysisStructDesc: _qbdibinder.bind('qbdi_getOperandAnalysisStructDesc', 'pointer', []),\n    getInstAnalysisStructDesc: _qbdibinder.bind('qbdi_getInstAnalysisStructDesc', 'pointer', []),\n    precacheBasicBlock: _qbdibinder.bind('qbdi_precacheBasicBlock', 'uchar', ['pointer', rword]),\n    clearCache: _qbdibinder.bind('qbdi_clearCache', 'void', ['pointer', rword, rword]),\n    clearAllCache: _qbdibinder.bind('qbdi_clearAllCache', 'void', ['pointer']),\n    getNbExecBlock: _qbdibinder.bind('qbdi_getNbExecBlock', 'uint32', ['pointer']),\n    reduceCacheTo: _qbdibinder.bind('qbdi_reduceCacheTo', 'void', ['pointer', 'uint32']),\n});\n\n// Init some globals\nif (Process.arch === 'x64') {\n    var GPR_NAMES_ = [\"RAX\", \"RBX\", \"RCX\", \"RDX\", \"RSI\", \"RDI\", \"R8\", \"R9\", \"R10\", \"R11\", \"R12\", \"R13\", \"R14\", \"R15\", \"RBP\", \"RSP\", \"RIP\", \"EFLAGS\", \"FS\", \"GS\"];\n    var REG_RETURN_ = \"RAX\";\n    var REG_PC_ = \"RIP\";\n    var REG_SP_ = \"RSP\";\n} else if (Process.arch === 'arm64') {\n    var GPR_NAMES_ = [\"X0\", \"X1\", \"X2\", \"X3\", \"X4\", \"X5\", \"X6\", \"X7\", \"X8\", \"X9\", \"X10\", \"X11\", \"X12\", \"X13\", \"X14\", \"X15\", \"X16\", \"X17\", \"X18\", \"X19\", \"X20\", \"X21\", \"X22\", \"X23\", \"X24\", \"X25\", \"X26\", \"X27\", \"X28\", \"FP\", \"LR\", \"SP\", \"NZCV\", \"PC\"];\n    var REG_RETURN_ = \"X0\";\n    var REG_PC_ = \"PC\";\n    var REG_SP_ = \"SP\";\n} else if (Process.arch === 'arm') {\n    var GPR_NAMES_ = [\"R0\", \"R1\", \"R2\", \"R3\", \"R4\", \"R5\", \"R6\", \"R7\", \"R8\", \"R9\", \"R10\", \"R11\", \"R12\", \"SP\", \"LR\", \"PC\", \"CPSR\"];\n    var REG_RETURN_ = \"R0\";\n    var REG_PC_ = \"PC\";\n    var REG_SP_ = \"SP\";\n} else if (Process.arch === 'ia32') {\n    var GPR_NAMES_ = [\"EAX\", \"EBX\", \"ECX\", \"EDX\", \"ESI\", \"EDI\", \"EBP\", \"ESP\", \"EIP\", \"EFLAGS\"];\n    var REG_RETURN_ = \"EAX\";\n    var REG_PC_ = \"EIP\";\n    var REG_SP_ = \"ESP\";\n}\n\n/**\n * An array holding register names.\n */\nexport var GPR_NAMES = GPR_NAMES_;\n/**\n * A constant string representing the register carrying the return value of a function.\n */\nexport var REG_RETURN = REG_RETURN_;\n/**\n * String of the instruction pointer register.\n */\nexport var REG_PC = REG_PC_;\n/**\n * String of the stack pointer register.\n */\nexport var REG_SP = REG_SP_;\n\n/**\n * Error return by the QBDI VM\n *\n * @enum {number}\n * @readonly\n */\nexport var VMError = Object.freeze({\n    /**\n     * Returned event is invalid.\n     */\n    INVALID_EVENTID: 0xffffffff\n});\n\n/**\n * Synchronisation direction between Frida and QBDI GPR contexts\n *\n * @enum {number}\n * @readonly\n */\nexport var SyncDirection = Object.freeze({\n    /**\n     * Constant variable used to synchronize QBDI's context to Frida's.\n     *\n     * .. warning:: This is currently not supported due to the lack of context updating in Frida.\n     */\n    QBDI_TO_FRIDA: 0,\n    /**\n     * Constant variable used to synchronize Frida's context to QBDI's.\n     */\n    FRIDA_TO_QBDI: 1\n});\n\n/**\n * The callback results.\n *\n * @enum {number}\n * @readonly\n */\nexport var VMAction = Object.freeze({\n    /**\n     * The execution of the basic block continues.\n     */\n    CONTINUE: 0,\n    /**\n     * Available only with PREINST InstCallback.  The instruction and the\n     * remained PREINST callbacks are skip. The execution continue with the\n     * POSTINST instruction.\n     *\n     * We recommand to used this result with a low priority PREINST callback in\n     * order to emulate the instruction without skipping the POSTINST callback.\n     */\n    SKIP_INST: 1,\n    /*!*\n     * Available only with InstCallback. The current instruction and the\n     * reminding callback (PRE and POST) are skip. The execution continues to\n     * the next instruction.\n     *\n     * For instruction that change the instruction pointer (jump/call/ret),\n     * BREAK_TO_VM must be used insted of SKIP.\n     *\n     * SKIP can break the record of MemoryAccess for the current instruction.\n     */\n    SKIP_PATCH: 2,\n    /**\n     * The execution breaks and returns to the VM causing a complete\n     * reevaluation of the execution state. A :js:data:`VMAction.BREAK_TO_VM` is\n     * needed to ensure that modifications of the Program Counter or the program\n     * code are taken into account.\n     */\n    BREAK_TO_VM: 3,\n    /**\n     * Stops the execution of the program. This causes the run function to\n     * return early.\n     */\n    STOP: 4\n});\n\n\n/**\n * Position relative to an instruction.\n *\n * @enum {number}\n * @readonly\n */\nexport var InstPosition = Object.freeze({\n    /**\n     * Positioned **before** the instruction.\n     */\n    PREINST: 0,\n    /**\n     * Positioned **after** the instruction.\n     */\n    POSTINST: 1\n});\n\n/**\n * Priority of callback\n *\n * @enum {number}\n * @readonly\n */\nexport var CallbackPriority = Object.freeze({\n    /**\n     * Default priority for callback.\n     */\n    PRIORITY_DEFAULT: 0,\n    /**\n     * Maximum priority if getInstMemoryAccess is used in the callback.\n     */\n    PRIORITY_MEMACCESS_LIMIT: 0x1000000\n});\n\n/**\n * Events triggered by the virtual machine.\n *\n * @enum {number}\n * @readonly\n */\nexport var VMEvent = Object.freeze({\n    /**\n     * Triggered when the execution enters a sequence.\n     */\n    SEQUENCE_ENTRY: 1,\n    /**\n     * Triggered when the execution exits from the current sequence.\n     */\n    SEQUENCE_EXIT: 1 << 1,\n    /**\n     * Triggered when the execution enters a basic block.\n     */\n    BASIC_BLOCK_ENTRY: 1 << 2,\n    /**\n     * Triggered when the execution exits from the current basic block.\n     */\n    BASIC_BLOCK_EXIT: 1 << 3,\n    /**\n     * Triggered when the execution enters a new (~unknown) basic block.\n     */\n    BASIC_BLOCK_NEW: 1 << 4,\n    /**\n     * Triggered when the ExecBroker executes an execution transfer.\n     */\n    EXEC_TRANSFER_CALL: 1 << 5,\n    /**\n     * Triggered when the ExecBroker returns from an execution transfer.\n     */\n    EXEC_TRANSFER_RETURN: 1 << 6,\n    /**\n     * Not implemented.\n     */\n    SYSCALL_ENTRY: 1 << 7,\n    /**\n     * Not implemented.\n     */\n    SYSCALL_EXIT: 1 << 8,\n    /**\n     * Not implemented.\n     */\n    SIGNAL: 1 << 9\n});\n\n/**\n * Memory access type (read / write / ...)\n *\n * @enum {number}\n * @readonly\n */\nexport var MemoryAccessType = Object.freeze({\n    /**\n     * Memory read access.\n     */\n    MEMORY_READ: 1,\n    /**\n     * Memory write access.\n     */\n    MEMORY_WRITE: 2,\n    /**\n     * Memory read/write access.\n     */\n    MEMORY_READ_WRITE: 3\n});\n\n/**\n * Memory access flags\n *\n * @enum {number}\n * @readonly\n */\nexport var MemoryAccessFlags = Object.freeze({\n    /**\n     * Empty flag.\n     */\n    MEMORY_NO_FLAGS: 0,\n    /**\n     * The size of the access isn't known.\n     */\n    MEMORY_UNKNOWN_SIZE: 1 << 0,\n    /**\n     * The given size is a minimum size.\n     */\n    MEMORY_MINIMUM_SIZE: 1 << 1,\n    /**\n     * The value of the access is unknown or hasn't been retrived.\n     */\n    MEMORY_UNKNOWN_VALUE: 1 << 2\n});\n\n/**\n * Register access type (read / write / rw)\n *\n * @enum {number}\n * @readonly\n */\nexport var RegisterAccessType = Object.freeze({\n    /**\n     * Register is read.\n     */\n    REGISTER_READ: 1,\n    /**\n     * Register is written.\n     */\n    REGISTER_WRITE: 2,\n    /**\n     * Register is read/written.\n     */\n    REGISTER_READ_WRITE: 3\n});\n\n/**\n * Instruction Condition\n *\n * @enum {number}\n * @readonly\n */\nexport var ConditionType = Object.freeze({\n    /**\n     * The instruction is unconditionnal\n     */\n    CONDITION_NONE: 0x0,\n    /**\n     * The instruction is always true\n     */\n    CONDITION_ALWAYS: 0x2,\n    /**\n     * The instruction is always false\n     */\n    CONDITION_NEVER: 0x3,\n    /**\n     * Equals ( '==' )\n     */\n    CONDITION_EQUALS: 0x4,\n    /**\n     * Not Equals ( '!=' )\n     */\n    CONDITION_NOT_EQUALS: 0x5,\n    /**\n     * Above ( '>' unsigned )\n     */\n    CONDITION_ABOVE: 0x6,\n    /**\n     * Below or Equals ( '<=' unsigned )\n     */\n    CONDITION_BELOW_EQUALS: 0x7,\n    /**\n     * Above or Equals ( '>=' unsigned )\n     */\n    CONDITION_ABOVE_EQUALS: 0x8,\n    /**\n     * Below ( '<' unsigned )\n     */\n    CONDITION_BELOW: 0x9,\n    /**\n     * Great ( '>' signed )\n     */\n    CONDITION_GREAT: 0xa,\n    /**\n     * Less or Equals ( '<=' signed )\n     */\n    CONDITION_LESS_EQUALS: 0xb,\n    /**\n     * Great or Equals ( '>=' signed )\n     */\n    CONDITION_GREAT_EQUALS: 0xc,\n    /**\n     * Less ( '<' signed )\n     */\n    CONDITION_LESS: 0xd,\n    /**\n     * Even\n     */\n    CONDITION_EVEN: 0xe,\n    /**\n     * Odd\n     */\n    CONDITION_ODD: 0xf,\n    /**\n     * Overflow\n     */\n    CONDITION_OVERFLOW: 0x10,\n    /**\n     * Not Overflow\n     */\n    CONDITION_NOT_OVERFLOW: 0x11,\n    /**\n     * Sign\n     */\n    CONDITION_SIGN: 0x12,\n    /**\n     * Not Sign\n     */\n    CONDITION_NOT_SIGN: 0x13\n});\n\n\n/**\n * Register access type (read / write / rw)\n *\n * @enum {number}\n * @readonly\n */\nexport var OperandType = Object.freeze({\n    /**\n     * Invalid operand.\n     */\n    OPERAND_INVALID: 0,\n    /**\n     * Immediate operand.\n     */\n    OPERAND_IMM: 1,\n    /**\n     * General purpose register operand.\n     */\n    OPERAND_GPR: 2,\n    /**\n     * Predicate special operand.\n     */\n    OPERAND_PRED: 3,\n    /**\n     * Float register operand.\n     */\n    OPERAND_FPR: 4,\n    /**\n     * Segment or unsupported register operand\n     */\n    OPERAND_SEG: 5\n});\n\n/**\n * Operand flag\n *\n * @enum {number}\n * @readonly\n */\nexport var OperandFlag = Object.freeze({\n    /**\n     * No flag\n     */\n    OPERANDFLAG_NONE: 0,\n    /**\n     * The operand is used to compute an address\n     */\n    OPERANDFLAG_ADDR: 1 << 0,\n    /**\n     * The value of the operand is PC relative\n     */\n    OPERANDFLAG_PCREL: 1 << 1,\n    /**\n     * The operand role isn't fully defined\n     */\n    OPERANDFLAG_UNDEFINED_EFFECT: 1 << 2,\n    /**\n     * The operand is implicit\n     */\n    OPERANDFLAG_IMPLICIT: 1 << 3\n});\n\n/**\n * Properties to retrieve during an instruction analysis.\n *\n * @enum {number}\n * @readonly\n */\nexport var AnalysisType = Object.freeze({\n    /**\n     * Instruction analysis (address, mnemonic, ...).\n     */\n    ANALYSIS_INSTRUCTION: 1,\n    /**\n     * Instruction disassembly.\n     */\n    ANALYSIS_DISASSEMBLY: 1 << 1,\n    /**\n     * Instruction operands analysis.\n     */\n    ANALYSIS_OPERANDS: 1 << 2,\n    /**\n     * Instruction nearest symbol (and offset).\n     */\n    ANALYSIS_SYMBOL: 1 << 3,\n    /**\n     * QBDI JIT Information.\n     */\n    ANALYSIS_JIT: 1 << 4\n});\n\n/**\n * QBDI VM Options\n *\n * @enum {number}\n * @readonly\n */\nexport var Options = {\n    /**\n     * Default value\n     */\n    NO_OPT: 0,\n    /**\n     * Disable all operation on FPU (SSE, AVX, SIMD).\n     * May break the execution if the target use the FPU.\n     */\n    OPT_DISABLE_FPR: 1 << 0,\n    /**\n     * Disable context switch optimisation when the target\n     * execblock doesn't used FPR.\n     */\n    OPT_DISABLE_OPTIONAL_FPR : 1 << 1,\n    /**\n     * Don't load the value when perform memory access.\n     */\n    OPT_DISABLE_MEMORYACCESS_VALUE : 1 << 2,\n    /**\n     * Don't save and restore errno.\n     */\n    OPT_DISABLE_ERRNO_BACKUP : 1 << 3,\n};\nif (Process.arch === 'x64') {\n    /**\n     * Used the AT&T syntax for instruction disassembly (for X86 and X86_64)\n     */\n    Options.OPT_ATT_SYNTAX = 1 << 24;\n    /**\n     * Enable Backup/Restore of FS/GS segment.\n     * This option uses the instructions (RD|WR)(FS|GS)BASE that must be\n     * supported by the operating system.\n     */\n    Options.OPT_ENABLE_FS_GS = 1 << 25;\n} else if (Process.arch === 'ia32') {\n    Options.OPT_ATT_SYNTAX = 1 << 24;\n} else if (Process.arch === 'arm64') {\n    /**\n     * Disable the emulation of the local monitor by QBDI\n     */\n    Options.OPT_DISABLE_LOCAL_MONITOR = 1 << 24;\n    /**\n     * Disable pointeur authentication\n     */\n    Options.OPT_BYPASS_PAUTH = 1 << 25;\n    /**\n     * Enable BTI on instrumented code\n     */\n    Options.OPT_ENABLE_BTI = 1 << 26;\n} else if (Process.arch === 'arm') {\n    Options.OPT_DISABLE_LOCAL_MONITOR = 1 << 24;\n    /**\n     * Disable the used of D16-D31 register\n     */\n    Options.OPT_DISABLE_D16_D31 = 1 << 25;\n    /**\n     * Change between ARM and Thumb as an ARMv4 CPU\n     */\n    Options.OPT_ARMv4 = 3 << 26;\n    /**\n     * Change between ARM and Thumb as an ARMv5T or ARMv6 CPU\n     */\n    Options.OPT_ARMv5T_6 = 1 << 27;\n    /**\n     * Change between ARM and Thumb as an ARMv7 CPU (default)\n     */\n    Options.OPT_ARMv7 = 0;\n    Options.OPT_ARM_MASK = 3 << 26;\n}\n\nOptions = Object.freeze(Options);\n\nexport class InstrRuleDataCBK {\n    /**\n     * Object to define an :js:func:`InstCallback` in an :js:func:`InstrRuleCallback`\n     *\n     * @param {InstPosition} pos       Relative position of the callback (PreInst / PostInst).\n     * @param {InstCallback} cbk       A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}       data      User defined data passed to the callback.\n     * @param {Int}          priority  The priority of the callback.\n     */\n    constructor(pos, cbk, data, priority = CallbackPriority.PRIORITY_DEFAULT) {\n        this.position = pos;\n        this.cbk = cbk;\n        this.data = data;\n        this.priority = priority;\n    }\n}\n\nclass State {\n    constructor(state) {\n        if (!NativePointer.prototype.isPrototypeOf(state) || state.isNull()) {\n            throw new TypeError('Invalid state pointer');\n        }\n        this.statePtr = state;\n    }\n\n    get ptr() {\n        return this.statePtr;\n    }\n\n    toRword() {\n        return this.statePtr.toRword();\n    }\n\n    toString() {\n        return this.statePtr.toString();\n    }\n}\n\n/**\n * General Purpose Register context\n */\nclass GPRState extends State {\n    _getGPRId(rid) {\n        if (typeof (rid) === 'string') {\n            rid = GPR_NAMES.indexOf(rid.toUpperCase());\n        }\n        if (rid < 0 || rid > GPR_NAMES.length) {\n            return undefined;\n        }\n        return rid;\n    }\n\n    /**\n     * This function is used to get the value of a specific register.\n     *\n     * @param {String|Number} rid Register (register name or ID can be used e.g : \"RAX\", \"rax\", 0)\n     *\n     * @return GPR value (ex: 0x42)\n     */\n    getRegister(rid) {\n        var rid = this._getGPRId(rid);\n        if (rid === null) {\n            return undefined;\n        }\n        return ptr(QBDI_C.getGPR(this.ptr, rid));\n    }\n\n    /**\n     * This function is used to set the value of a specific register.\n     *\n     * @param {String|Number} rid   Register (register name or ID can be used e.g : \"RAX\", \"rax\", 0)\n     * @param {String|Number} value Register value (use **strings** for big integers)\n     */\n    setRegister(rid, value) {\n        var rid = this._getGPRId(rid);\n        if (rid !== null) {\n            QBDI_C.setGPR(this.ptr, rid, value.toRword());\n        }\n    }\n\n    /**\n     * This function is used to get values of all registers.\n     *\n     * @return GPRs of current context (ex: \\{\"RAX\":0x42, ...\\})\n     */\n    getRegisters() {\n        var regCnt = GPR_NAMES.length;\n        var gprs = {};\n        for (var i = 0; i < regCnt; i++) {\n            gprs[GPR_NAMES[i]] = this.getRegister(i);\n        }\n        return gprs;\n    }\n\n    /**\n     * This function is used to set values of all registers.\n     *\n     * @param gprs Array of register values\n     */\n    setRegisters(gprs) {\n        var regCnt = GPR_NAMES.length;\n        for (var i = 0; i < regCnt; i++) {\n            this.setRegister(i, gprs[GPR_NAMES[i]]);\n        }\n    }\n\n    /**\n     * This function is used to synchronise a specific register between Frida and QBDI.\n     *\n     * .. warning:: Currently QBDI_TO_FRIDA is experimental. (E.G : RIP cannot be synchronized)\n     *\n     * @param                   FridaCtx   Frida context\n     * @param {String|Number}   rid        Register (register name or ID can be used e.g : \"RAX\", \"rax\", 0)\n     * @param {SyncDirection}   direction  Synchronization direction. (:js:data:`FRIDA_TO_QBDI` or :js:data:`QBDI_TO_FRIDA`)\n     */\n    synchronizeRegister(FridaCtx, rid, direction) {\n        if (direction === SyncDirection.FRIDA_TO_QBDI) {\n            this.setRegister(rid, FridaCtx[rid.toLowerCase()].toRword());\n        }\n        else { // FRIDA_TO_QBDI\n            FridaCtx[rid.toLowerCase()] = ptr(this.getRegister(rid).toString());\n        }\n    }\n\n    /**\n     * This function is used to synchronise context between Frida and QBDI.\n     *\n     * .. warning:: Currently QBDI_TO_FRIDA is not implemented (due to Frida limitations).\n     *\n     * @param                   FridaCtx   Frida context\n     * @param {SyncDirection}   direction  Synchronization direction. (:js:data:`FRIDA_TO_QBDI` or :js:data:`QBDI_TO_FRIDA`)\n     */\n    synchronizeContext(FridaCtx, direction) {\n        for (var i in GPR_NAMES) {\n            if (GPR_NAMES[i] === \"EFLAGS\" || GPR_NAMES[i] === \"FS\" || GPR_NAMES[i] === \"GS\") {\n                continue;\n            }\n            this.synchronizeRegister(FridaCtx, GPR_NAMES[i], direction);\n        }\n        if (direction === SyncDirection.QBDI_TO_FRIDA) {\n            throw new Error('Not implemented (does not really work due to Frida)');\n        }\n    }\n\n    /**\n     * Pretty print QBDI context.\n     *\n     * @param {bool} [color] Will print a colored version of the context if set.\n     *\n     * @return dump of all GPRs in a pretty format\n     */\n    pp(color) {\n        var RED = color ? \"\\x1b[31m\" : \"\";\n        var GREEN = color ? \"\\x1b[32m\" : \"\";\n        var RESET = color ? \"\\x1b[0m\" : \"\";\n        var regCnt = GPR_NAMES.length;\n        var regs = this.getRegisters();\n        var line = \"\";\n        for (var i = 0; i < regCnt; i++) {\n            var name = GPR_NAMES[i];\n            if (!(i % 4) && i) {\n                line += '\\n';\n            }\n            line += GREEN; // Will be overwritten by RED if necessary\n            if (name === \"RIP\" | name === \"PC\") {\n                line += RED;\n            }\n            line += name.leftPad(\"    \") + RESET + \"=0x\" + hexPointer(regs[name]) + \" \";\n        }\n        return line;\n    }\n\n    /**\n     * Pretty print and log QBDI context.\n     *\n     * @param {bool} [color] Will print a colored version of the context if set.\n     */\n    dump(color) {\n        console.log(this.pp(color));\n    }\n\n    static validOrThrow(state) {\n        if (!GPRState.prototype.isPrototypeOf(state)) {\n            throw new TypeError('Invalid GPRState');\n        }\n    }\n}\n\n/**\n * Floating Point Register context\n */\nclass FPRState extends State {\n    static validOrThrow(state) {\n        if (!FPRState.prototype.isPrototypeOf(state)) {\n            throw new TypeError('Invalid FPRState');\n        }\n    }\n}\n\nexport class VM {\n    // private member\n    #vm = null;\n    #memoryAccessDesc = null;\n    #operandAnalysisStructDesc = null;\n    #instAnalysisStructDesc = null;\n    #vmStateStructDesc = null;\n    #userDataPtrMap = {};\n    #userDataIIdMap = {};\n    #userDataPointer = 0;\n\n    /**\n     * Create a new instrumentation virtual machine using \"**new VM()**\"\n     */\n    constructor() {\n        // Enforce a minimum QBDI version (API compatibility)\n        if (!this.version || this.version.integer < QBDI_MINIMUM_VERSION) {\n            throw new Error('Invalid QBDI version !');\n        }\n\n        // Create VM instance\n        this.#vm = this._initVM();\n\n        // Cache various remote structure descriptions\n        // Parse remote structure descriptions\n        this.#memoryAccessDesc = this._parseStructDesc(QBDI_C.getMemoryAccessStructDesc());\n        this.#operandAnalysisStructDesc = this._parseStructDesc(QBDI_C.getOperandAnalysisStructDesc());\n        this.#instAnalysisStructDesc = this._parseStructDesc(QBDI_C.getInstAnalysisStructDesc());\n        this.#vmStateStructDesc = this._parseStructDesc(QBDI_C.getVMStateStructDesc());\n\n        // add a destructor on garbage collection\n        // The name of the API change with frida 15.0.0\n        if (Number(Frida.version.split(\".\")[0]) < 15) {\n            var that = this;\n            WeakRef.bind(VM, function dispose() {\n                if (that.ptr !== null) {\n                    that._terminateVM(that.ptr);\n                }\n            });\n        } else {\n            var that = this;\n            Script.bindWeak(VM, function dispose() {\n                if (that.ptr !== null) {\n                    that._terminateVM(that.ptr);\n                }\n            });\n        }\n    }\n\n    get ptr() {\n        return this.#vm;\n    }\n\n    /**\n     * QBDI version (major, minor, patch).\n     *\n     * {string:String, integer:Number, major:Number, minor:Number, patch:Number}\n     */\n    get version() {\n        if (!QBDI_C.getVersion) {\n            return undefined;\n        }\n        var version = {};\n        var versionPtr = Memory.alloc(4);\n        var vStrPtr = QBDI_C.getVersion(versionPtr);\n        var vInt = versionPtr.readU32();\n        version.string = vStrPtr.readCString();\n        version.integer = vInt;\n        version.major = (vInt >> 16) & 0xff;\n        version.minor = (vInt >> 8) & 0xff;\n        version.patch = vInt & 0xff;\n        Object.freeze(version);\n        return version;\n    }\n\n    /**\n     * Get the current options of the VM\n     *\n     * @return  {Options}  The current option\n     */\n    getOptions() {\n        return QBDI_C.getOptions(this.#vm);\n    }\n\n    /**\n     * Set the options of the VM\n     *\n     * @param  {Options}  options  The new options of the VM.\n     */\n    setOptions(options) {\n        QBDI_C.setOptions(this.#vm, options);\n    }\n\n    /**\n     * Add an address range to the set of instrumented address ranges.\n     *\n     * @param {String|Number|NativePointer} start  Start address of the range (included).\n     * @param {String|Number|NativePointer} end    End address of the range (excluded).\n     */\n    addInstrumentedRange(start, end) {\n        QBDI_C.addInstrumentedRange(this.#vm, start.toRword(), end.toRword());\n    }\n\n    /**\n     * Add the executable address ranges of a module to the set of instrumented address ranges.\n     *\n     * @param  {String} name   The module's name.\n     *\n     * @return {bool} True if at least one range was added to the instrumented ranges.\n     */\n    addInstrumentedModule(name) {\n        var namePtr = Memory.allocUtf8String(name);\n        return QBDI_C.addInstrumentedModule(this.#vm, namePtr) == true;\n    }\n\n    /**\n     * Add the executable address ranges of a module to the set of instrumented address ranges. using an address belonging to the module.\n     *\n     * @param  {String|Number|NativePointer} addr An address contained by module's range.\n     *\n     * @return {bool} True if at least one range was removed from the instrumented ranges.\n     */\n    addInstrumentedModuleFromAddr(addr) {\n        return QBDI_C.addInstrumentedModuleFromAddr(this.#vm, addr.toRword()) == true;\n    }\n\n    /**\n     * Adds all the executable memory maps to the instrumented range set.\n     *\n     * @return {bool} True if at least one range was added to the instrumented ranges.\n     */\n    instrumentAllExecutableMaps() {\n        return QBDI_C.instrumentAllExecutableMaps(this.#vm) == true;\n    }\n\n    /**\n     * Remove an address range from the set of instrumented address ranges.\n     *\n     * @param {String|Number|NativePointer} start  Start address of the range (included).\n     * @param {String|Number|NativePointer} end    End address of the range (excluded).\n     */\n    removeInstrumentedRange(start, end) {\n        QBDI_C.removeInstrumentedRange(this.#vm, start.toRword(), end.toRword());\n    }\n\n    /**\n     * Remove the executable address ranges of a module from the set of instrumented address ranges.\n     *\n     * @param {String} name   The module's name.\n     *\n     * @return {bool} True if at least one range was added to the instrumented ranges.\n     */\n    removeInstrumentedModule(name) {\n        var namePtr = Memory.allocUtf8String(name);\n        return QBDI_C.removeInstrumentedModule(this.#vm, namePtr) == true;\n    }\n\n    /**\n     * Remove the executable address ranges of a module from the set of instrumented address ranges using an address belonging to the module.\n     *\n     * @param {String|Number|NativePointer} addr: An address contained by module's range.\n     *\n     * @return {bool} True if at least one range was added to the instrumented ranges.\n     */\n    removeInstrumentedModuleFromAddr(addr) {\n        return QBDI_C.removeInstrumentedModuleFromAddr(this.#vm, addr.toRword()) == true;\n    }\n\n    /**\n     * Remove all instrumented ranges.\n     */\n    removeAllInstrumentedRanges() {\n        QBDI_C.removeAllInstrumentedRanges(this.#vm);\n    }\n\n    /**\n     * Start the execution by the DBI from a given address (and stop when another is reached).\n     *\n     * @param {String|Number|NativePointer} start  Address of the first instruction to execute.\n     * @param {String|Number|NativePointer} stop   Stop the execution when this instruction is reached.\n     *\n     * @return {bool} True if at least one block has been executed.\n     */\n    run(start, stop) {\n        return QBDI_C.run(this.#vm, start.toRword(), stop.toRword()) == true;\n    }\n\n    /**\n     * Obtain the current general register state.\n     *\n     * @return {GPRState} An object containing the General Purpose Registers state.\n     */\n    getGPRState() {\n        return new GPRState(QBDI_C.getGPRState(this.#vm));\n    }\n\n    /**\n     * Obtain the current floating point register state.\n     *\n     * @return {FPRState} An object containing the Floating point Purpose Registers state.\n     */\n    getFPRState() {\n        return new FPRState(QBDI_C.getFPRState(this.#vm));\n    }\n\n    /**\n     * Get the backuped value of errno.\n     *\n     * @return {Integer} errno\n     */\n    getErrno() {\n        return QBDI_C.getErrno(this.#vm);\n    }\n\n    /**\n     * Set the GPR state\n     *\n     * @param {GPRState} state  Array of register values\n     */\n    setGPRState(state) {\n        GPRState.validOrThrow(state);\n        QBDI_C.setGPRState(this.#vm, state.ptr);\n    }\n\n    /**\n     * Set the FPR state\n     *\n     * @param {FPRState} state  Array of register values\n     */\n    setFPRState(state) {\n        FPRState.validOrThrow(state);\n        QBDI_C.setFPRState(this.#vm, state.ptr);\n    }\n\n    /**\n     * Set the backuped value of errno\n     *\n     * @param {Integer} errno  the value to set\n     */\n    setErrno(errno) {\n        QBDI_C.setErrno(this.#vm, errno);\n    }\n\n    /**\n     * Pre-cache a known basic block.\n     *\n     * @param {String|Number|NativePointer} pc  Start address of a basic block\n     *\n     * @return {bool} True if basic block has been inserted in cache.\n     */\n    precacheBasicBlock(pc) {\n        return QBDI_C.precacheBasicBlock(this.#vm, pc) == true\n    }\n\n    /**\n     * Clear a specific address range from the translation cache.\n     *\n     * @param {String|Number|NativePointer}  start  Start of the address range to clear from the cache.\n     * @param {String|Number|NativePointer}  end    End of the address range to clear from the cache.\n     */\n    clearCache(start, end) {\n        QBDI_C.clearCache(this.#vm, start, end)\n    }\n\n    /**\n     * Clear the entire translation cache.\n     */\n    clearAllCache() {\n        QBDI_C.clearAllCache(this.#vm)\n    }\n\n    /**\n     * Get the number of ExecBlock in the cache. Each block uses 2 memory pages\n     * and some heap allocations.\n     *\n     * @return {Integer} The number of ExecBlock in the cache.\n     */\n    getNbExecBlock() {\n        return QBDI_C.getNbExecBlock(this.#vm)\n    }\n\n    /** \n     * Reduce the cache to X ExecBlock. Note that this will try to purge the\n     * oldest ExecBlock first, but the block may be recreate if needed by\n     * followed execution.\n     *\n     * @param {Integer} nb The number of BasicBlock that should remains in the\n     *                     cache\n     */\n    reduceCacheTo(nb) {\n        return QBDI_C.reduceCacheTo(this.#vm, nb)\n    }\n\n    /**\n     * Register a callback event if the instruction matches the mnemonic.\n     *\n     * @param {String}       mnem      Mnemonic to match.\n     * @param {InstPosition} pos       Relative position of the callback (PreInst / PostInst).\n     * @param {InstCallback} cbk       A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}       data      User defined data passed to the callback.\n     * @param {Int}          priority  The priority of the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addMnemonicCB(mnem, pos, cbk, data, priority = CallbackPriority.PRIORITY_DEFAULT) {\n        var mnemPtr = Memory.allocUtf8String(mnem);\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addMnemonicCB(vm, mnemPtr, pos, cbk, dataPtr, priority);\n        });\n    }\n\n    /**\n     * Register a callback event for every memory access matching the type bitfield made by the instruction in the range codeStart to codeEnd.\n     *\n     * @param {MemoryAccessType} type      A mode bitfield: either MEMORY_READ, MEMORY_WRITE or both (MEMORY_READ_WRITE).\n     * @param {InstCallback}     cbk       A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}           data      User defined data passed to the callback.\n     * @param {Int}              priority  The priority of the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addMemAccessCB(type, cbk, data, priority = CallbackPriority.PRIORITY_DEFAULT) {\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addMemAccessCB(vm, type, cbk, dataPtr, priority);\n        });\n    }\n\n    /**\n     * Add a custom instrumentation rule to the VM.\n     *\n     * @param {InstrRuleCallback}  cbk    A **native** InstrRuleCallback returned by :js:func:`VM.newInstrRuleCallback`.\n     * @param {AnalysisType}       type   Analyse type needed for this instruction function pointer to the callback\n     * @param {Object|null}             data   User defined data passed to the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addInstrRule(cbk, type, data) {\n        var vm = this.#vm;\n        return this._retainUserDataForInstrRuleCB(data, function (dataPtr) {\n            return QBDI_C.addInstrRule(vm, cbk, type, dataPtr);\n        });\n    }\n\n    /**\n     * Add a custom instrumentation rule to the VM for a range of address.\n     *\n     * @param {String|Number|NativePointer}      start  Begin of the range of address where apply the rule\n     * @param {String|Number|NativePointer}      end    End of the range of address where apply the rule\n     * @param {InstrRuleCallback}  cbk    A **native** InstrRuleCallback returned by :js:func:`VM.newInstrRuleCallback`.\n     * @param {AnalysisType}       type   Analyse type needed for this instruction function pointer to the callback\n     * @param {Object|null}             data   User defined data passed to the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addInstrRuleRange(start, end, cbk, type, data) {\n        var vm = this.#vm;\n        return this._retainUserDataForInstrRuleCB(data, function (dataPtr) {\n            return QBDI_C.addInstrRuleRange(vm, start.toRword(), end.toRword(), cbk, type, dataPtr);\n        });\n    }\n\n    /**\n     * Add a virtual callback which is triggered for any memory access at a specific address matching the access type.\n     * Virtual callbacks are called via callback forwarding by a gate callback triggered on every memory access. This incurs a high performance cost.\n     *\n     * @param {String|Number|NativePointer}     addr   Code address which will trigger the callback.\n     * @param {MemoryAccessType}  type   A mode bitfield: either MEMORY_READ, MEMORY_WRITE or both (MEMORY_READ_WRITE).\n     * @param {InstCallback}      cbk    A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}            data   User defined data passed to the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addMemAddrCB(addr, type, cbk, data) {\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addMemAddrCB(vm, addr.toRword(), type, cbk, dataPtr);\n        });\n    }\n\n    /**\n     * Add a virtual callback which is triggered for any memory access in a specific address range matching the access type.\n     * Virtual callbacks are called via callback forwarding by a gate callback triggered on every memory access. This incurs a high performance cost.\n     *\n     * @param {String|Number|NativePointer}     start    Start of the address range which will trigger the callback.\n     * @param {String|Number|NativePointer}     end      End of the address range which will trigger the callback.\n     * @param {MemoryAccessType}  type     A mode bitfield: either MEMORY_READ, MEMORY_WRITE or both (MEMORY_READ_WRITE).\n     * @param {InstCallback}      cbk      A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}            data     User defined data passed to the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addMemRangeCB(start, end, type, cbk, data) {\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addMemRangeCB(vm, start.toRword(), end.toRword(), type, cbk, dataPtr);\n        });\n    }\n\n    /**\n     * Register a callback event for a specific instruction event.\n     *\n     * @param {InstPosition} pos       Relative position of the callback (PreInst / PostInst).\n     * @param {InstCallback} cbk       A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}       data      User defined data passed to the callback.\n     * @param {Int}          priority  The priority of the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addCodeCB(pos, cbk, data, priority = CallbackPriority.PRIORITY_DEFAULT) {\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addCodeCB(vm, pos, cbk, dataPtr, priority);\n        });\n    }\n\n    /**\n     * Register a callback for when a specific address is executed.\n     *\n     * @param {String|Number|NativePointer} addr      Code address which will trigger the callback.\n     * @param {InstPosition}  pos       Relative position of the callback (PreInst / PostInst).\n     * @param {InstCallback}  cbk       A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}        data      User defined data passed to the callback.\n     * @param {Int}           priority  The priority of the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addCodeAddrCB(addr, pos, cbk, data, priority = CallbackPriority.PRIORITY_DEFAULT) {\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addCodeAddrCB(vm, addr.toRword(), pos, cbk, dataPtr, priority);\n        });\n    }\n\n    /**\n     * Register a callback for when a specific address range is executed.\n     *\n     * @param {String|Number|NativePointer} start     Start of the address range which will trigger the callback.\n     * @param {String|Number|NativePointer} end       End of the address range which will trigger the callback.\n     * @param {InstPosition}  pos       Relative position of the callback (PreInst / PostInst).\n     * @param {InstCallback}  cbk       A **native** InstCallback returned by :js:func:`VM.newInstCallback`.\n     * @param {Object|null}        data      User defined data passed to the callback.\n     * @param {Int}           priority  The priority of the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addCodeRangeCB(start, end, pos, cbk, data, priority = CallbackPriority.PRIORITY_DEFAULT) {\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addCodeRangeCB(vm, start.toRword(), end.toRword(), pos, cbk, dataPtr, priority);\n        });\n    }\n\n    /**\n     * Register a callback event for a specific VM event.\n     *\n     * @param {VMEvent}    mask   A mask of VM event type which will trigger the callback.\n     * @param {VMCallback} cbk    A **native** VMCallback returned by :js:func:`VM.newVMCallback`.\n     * @param {Object|null}     data   User defined data passed to the callback.\n     *\n     * @return {Number} The id of the registered instrumentation (or VMError.INVALID_EVENTID in case of failure).\n     */\n    addVMEventCB(mask, cbk, data) {\n        var vm = this.#vm;\n        return this._retainUserData(data, function (dataPtr) {\n            return QBDI_C.addVMEventCB(vm, mask, cbk, dataPtr);\n        });\n    }\n\n    /**\n     * Remove an instrumentation.\n     *\n     * @param   {Number} id   The id of the instrumentation to remove.\n     * @return  {bool} True if instrumentation has been removed.\n     */\n    deleteInstrumentation(id) {\n        this._releaseUserData(id);\n        return QBDI_C.deleteInstrumentation(this.#vm, id) == true;\n    }\n\n    /**\n     * Remove all the registered instrumentations.\n     */\n    deleteAllInstrumentations() {\n        this._releaseAllUserData();\n        QBDI_C.deleteAllInstrumentations(this.#vm);\n    }\n\n    /**\n     * Obtain the analysis of the current instruction. Analysis results are cached in the VM.\n     * The validity of the returned pointer is only guaranteed until the end of the callback, else a deepcopy of the structure is required.\n     *\n     * @param {AnalysisType} [type] Properties to retrieve during analysis (default to ANALYSIS_INSTRUCTION | ANALYSIS_DISASSEMBLY).\n     *\n     * @return {InstAnalysis} A :js:class:`InstAnalysis` object containing the analysis result.\n     */\n    getInstAnalysis(type) {\n        type = type || (AnalysisType.ANALYSIS_INSTRUCTION | AnalysisType.ANALYSIS_DISASSEMBLY);\n        var analysis = QBDI_C.getInstAnalysis(this.#vm, type);\n        if (analysis.isNull()) {\n            return NULL;\n        }\n        return this._parseInstAnalysis(analysis);\n    }\n\n    /**\n     * Obtain the analysis of a cached instruction. Analysis results are cached in the VM.\n     * The validity of the returned pointer is only guaranteed until the end of the callback, else a deepcopy of the structure is required.\n     *\n     * @param {String|Number|NativePointer} addr    The address of the instruction to analyse.\n     * @param {AnalysisType}  [type]  Properties to retrieve during analysis (default to ANALYSIS_INSTRUCTION | ANALYSIS_DISASSEMBLY).\n     *\n     * @return {InstAnalysis} A :js:class:`InstAnalysis` object containing the analysis result. null if the instruction isn't in the cache.\n     */\n    getCachedInstAnalysis(addr, type) {\n        type = type || (AnalysisType.ANALYSIS_INSTRUCTION | AnalysisType.ANALYSIS_DISASSEMBLY);\n        var analysis = QBDI_C.getCachedInstAnalysis(this.#vm, addr.toRword(), type);\n        if (analysis.isNull()) {\n            return NULL;\n        }\n        return this._parseInstAnalysis(analysis);\n    }\n\n    /**\n     * Obtain the analysis of a JITed instruction. Analysis results are cached\n     * in the VM. The validity of the returned pointer is only guaranteed until\n     * the end of the callback or a call to a noconst method of the VM object.\n     * This API may be used to determine if a given address of the current\n     * process memory correspond to the JIT patch from this VM. Note that this\n     * call may allocate memory.\n     *\n     * @param {String|Number|NativePointer} addr    The JIT address\n     * @param {AnalysisType}  [type]  Properties to retrieve during analysis (default to ANALYSIS_INSTRUCTION | ANALYSIS_DISASSEMBLY).\n     *\n     * @return {InstAnalysis} A :js:class:`InstAnalysis` object containing the analysis result. null if the instruction isn't in the cache.\n     */\n    getJITInstAnalysis(addr, type) {\n        type = type || (AnalysisType.ANALYSIS_INSTRUCTION | AnalysisType.ANALYSIS_DISASSEMBLY | AnalysisType.ANALYSIS_JIT);\n        var analysis = QBDI_C.getJITInstAnalysis(this.#vm, addr.toRword(), type);\n        if (analysis.isNull()) {\n            return NULL;\n        }\n        return this._parseInstAnalysis(analysis);\n    }\n\n    /**\n     * Obtain the memory accesses made by the last executed instruction. Return NULL and a size of 0 if the instruction made no memory access.\n     *\n     * @param {MemoryAccessType} type Memory mode bitfield to activate the logging for: either MEMORY_READ, MEMORY_WRITE or both (MEMORY_READ_WRITE).\n     */\n    recordMemoryAccess(type) {\n        return QBDI_C.recordMemoryAccess(this.#vm, type) == true;\n    }\n\n    /**\n     * Obtain the memory accesses made by the last executed instruction. Return NULL and a size of 0 if the instruction made no memory access.\n     *\n     * @return {MemoryAccess[]} An array of :js:class:`MemoryAccess` made by the instruction.\n     */\n    getInstMemoryAccess() {\n        return this._getMemoryAccess(QBDI_C.getInstMemoryAccess);\n    }\n\n    /**\n     * Obtain the memory accesses made by the last executed sequence. Return NULL and a size of 0 if the basic block made no memory access.\n     *\n     * @return {MemoryAccess[]} An array of :js:class:`MemoryAccess` made by the sequence.\n     */\n    getBBMemoryAccess() {\n        return this._getMemoryAccess(QBDI_C.getBBMemoryAccess);\n    }\n\n    // Memory\n\n    /**\n     * Allocate a new stack and setup the GPRState accordingly. The allocated stack needs to be freed with alignedFree().\n     *\n     * @param {GPRState} state      Array of register values\n     * @param {Number}   stackSize  Size of the stack to be allocated.\n     *\n     * @return  Pointer (rword) to the allocated memory or NULL in case an error was encountered.\n     */\n    allocateVirtualStack(state, stackSize) {\n        GPRState.validOrThrow(state);\n        var stackPtr = Memory.alloc(Process.pointerSize);\n        var ret = QBDI_C.allocateVirtualStack(state.ptr, stackSize, stackPtr);\n        if (ret == false) {\n            return NULL;\n        }\n        return stackPtr.readPointer();\n    }\n\n\n    /**\n     * Allocate a block of memory of a specified sized with an aligned base address.\n     *\n     * @param {Number} size   Allocation size in bytes.\n     * @param {Number} align  Base address alignement in bytes.\n     *\n     * @return  Pointer (rword) to the allocated memory or NULL in case an error was encountered.\n     */\n    alignedAlloc(size, align) {\n        return QBDI_C.alignedAlloc(size, align);\n    }\n\n    /**\n     * Free a block of aligned memory allocated with alignedAlloc or allocateVirtualStack\n     *\n     * @param {NativePtr} ptr  Pointer to the allocated memory.\n     */\n    alignedFree(ptr) {\n        QBDI_C.alignedFree(ptr);\n    }\n\n    /**\n     * Simulate a call by modifying the stack and registers accordingly.\n     *\n     * @param {GPRState}                state     Array of register values\n     * @param {String|Number|NativePointer}           retAddr   Return address of the call to simulate.\n     * @param {StringArray|NumberArray} args      A variadic list of arguments.\n     */\n    simulateCall(state, retAddr, args) {\n        GPRState.validOrThrow(state);\n        retAddr = retAddr.toRword();\n        var fargs = this._formatVAArgs(args);\n        // Use this weird construction to work around a bug in the duktape runtime\n        var _simulateCall = function (a, b, c, d, e, f, g, h, i, j) {\n            QBDI_C.simulateCall(state.ptr, retAddr, fargs[0], a, b, c, d, e, f, g, h, i, j);\n        }\n        _simulateCall.apply(null, fargs[1]);\n    }\n\n    /**\n     * Use QBDI engine to retrieve loaded modules.\n     *\n     * @return list of module names (ex: [\"ls\", \"libc\", \"libz\"])\n     */\n    getModuleNames() {\n        var sizePtr = Memory.alloc(4);\n        var modsPtr = QBDI_C.getModuleNames(sizePtr);\n        var size = sizePtr.readU32();\n        if (modsPtr.isNull() || size === 0) {\n            return [];\n        }\n        var mods = [];\n        var p = modsPtr;\n        for (var i = 0; i < size; i++) {\n            var strPtr = p.readPointer();\n            var str = strPtr.readCString();\n            mods.push(str);\n            System.free(strPtr);\n            p = p.add(Process.pointerSize);\n        }\n        System.free(modsPtr);\n        return mods;\n    }\n\n    // Logs\n    setLogPriority(priority) {\n        QBDI_C.setLogPriority(priority);\n    }\n\n    // Helpers\n\n    /**\n     * This callback is displayed as part of the Requester class.\n     * @callback InstrRuleCallbackRaw\n     * @param {VM} vm\n     * @param {InstAnalysis} ana\n     * @param {*} data\n     */\n\n    /**\n     * Create a native **Instruction rule callback** from a JS function.\n     *\n     * Example:\n     *       >>> var icbk = vm.newInstrRuleCallback(function(vm, ana, data) {\n     *       >>>   console.log(\"0x\" + ana.address.toString(16) + \" \" + ana.disassembly);\n     *       >>>   return [new InstrRuleDataCBK(InstPosition.POSTINST, printCB, ana.disassembly)];\n     *       >>> });\n     *\n     * @param {InstrRuleCallbackRaw} cbk an instruction callback (ex: function(vm, ana, data) {};)\n     *\n     * @return an native InstrRuleCallback\n     */\n    newInstrRuleCallback(cbk) {\n        if (typeof (cbk) !== 'function' || cbk.length !== 3) {\n            return undefined;\n        }\n        // Use a closure to provide object\n        var vm = this;\n        var jcbk = function (vmPtr, anaPtr, cbksPtr, dataPtr) {\n            var ana = vm._parseInstAnalysis(anaPtr);\n            var data = vm._getUserData(dataPtr);\n            var res = cbk(vm, ana, data.userdata);\n            if (res === null) {\n                return;\n            }\n            if (!Array.isArray(res)) {\n                throw new TypeError('Invalid InstrRuleDataCBK Array');\n            }\n            if (res.length === 0) {\n                return;\n            }\n            for (var i = 0; i < res.length; i++) {\n                var d = vm._retainUserDataForInstrRuleCB2(res[i].data, data.id);\n                QBDI_C.addInstrRuleData(cbksPtr, res[i].position, res[i].cbk, d, res[i].priority);\n            }\n        }\n        return new NativeCallback(jcbk, 'void', ['pointer', 'pointer', 'pointer', 'pointer']);\n    }\n\n    /**\n     * This callback is displayed as part of the Requester class.\n     * @callback InstCallbackRaw\n     * @param {VM} vm\n     * @param {GPRState} gpr\n     * @param {FPRState} fpr\n     * @param {*} data\n     */\n\n    /**\n     * Create a native **Instruction callback** from a JS function.\n     *\n     * Example:\n     *       >>> var icbk = vm.newInstCallback(function(vm, gpr, fpr, data) {\n     *       >>>   inst = vm.getInstAnalysis();\n     *       >>>   console.log(\"0x\" + inst.address.toString(16) + \" \" + inst.disassembly);\n     *       >>>   return VMAction.CONTINUE;\n     *       >>> });\n     *\n     * @param {InstCallbackRaw} cbk an instruction callback (ex: function(vm, gpr, fpr, data) {};)\n     *\n     * @return an native InstCallback\n     */\n    newInstCallback(cbk) {\n        if (typeof (cbk) !== 'function' || cbk.length !== 4) {\n            return undefined;\n        }\n        // Use a closure to provide object\n        var vm = this;\n        var jcbk = function (vmPtr, gprPtr, fprPtr, dataPtr) {\n            var gpr = new GPRState(gprPtr);\n            var fpr = new FPRState(fprPtr);\n            var data = vm._getUserData(dataPtr);\n            return cbk(vm, gpr, fpr, data);\n        }\n        return new NativeCallback(jcbk, 'int', ['pointer', 'pointer', 'pointer', 'pointer']);\n    }\n\n    /**\n     * This callback is displayed as part of the Requester class.\n     * @callback VMCallbackRaw\n     * @param {VM} vm\n     * @param {VMState} state\n     * @param {GPRState} gpr\n     * @param {FPRState} fpr\n     * @param {*} data\n     */\n\n    /**\n     * Create a native **VM callback** from a JS function.\n     *\n     * Example:\n     *       >>> var vcbk = vm.newVMCallback(function(vm, evt, gpr, fpr, data) {\n     *       >>>   if (evt.event & VMEvent.EXEC_TRANSFER_CALL) {\n     *       >>>     console.warn(\"[!] External call to 0x\" + evt.basicBlockStart.toString(16));\n     *       >>>   }\n     *       >>>   return VMAction.CONTINUE;\n     *       >>> });\n     *\n     * @param {VMCallbackRaw} cbk a VM callback (ex: function(vm, state, gpr, fpr, data) {};)\n     *\n     * @return a native VMCallback\n     */\n    newVMCallback(cbk) {\n        if (typeof (cbk) !== 'function' || cbk.length !== 5) {\n            return undefined;\n        }\n        // Use a closure to provide object and a parsed event\n        var vm = this;\n        var jcbk = function (vmPtr, state, gprPtr, fprPtr, dataPtr) {\n            var s = vm._parseVMState(state);\n            var gpr = new GPRState(gprPtr);\n            var fpr = new FPRState(fprPtr);\n            var data = vm._getUserData(dataPtr);\n            return cbk(vm, s, gpr, fpr, data);\n        }\n        return new NativeCallback(jcbk, 'int', ['pointer', 'pointer', 'pointer', 'pointer', 'pointer']);\n    }\n\n    /**\n     * Call a function by its address (or through a Frida ``NativePointer``).\n     *\n     * Arguments can be provided, but their types need to be compatible\n     * with the ``.toRword()`` interface (like ``NativePointer`` or ``UInt64``).\n     *\n     * Example:\n     *       >>> var vm = new VM();\n     *       >>> var state = vm.getGPRState();\n     *       >>> var stackTopPtr = vm.allocateVirtualStack(state, 0x1000000);\n     *       >>> var aFunction = Module.findGlobalExportByName(\"Secret\");\n     *       >>> vm.addInstrumentedModuleFromAddr(aFunction);\n     *       >>> vm.call(aFunction, [42]);\n     *       >>> vm.alignedFree(stackTopPtr);\n     *\n     * @param {String|Number|NativePointer}           address function address (or Frida ``NativePointer``).\n     * @param {StringArray|NumberArray} [args]  optional list of arguments\n     */\n    call(address, args) {\n        address = address.toRword();\n        var fargs = this._formatVAArgs(args);\n        var vm = this.#vm;\n        // Use this weird construction to work around a bug in the duktape runtime\n        var _call = function (a, b, c, d, e, f, g, h, i, j) {\n            var retPtr = Memory.alloc(Process.pointerSize);\n            var res = QBDI_C.call(vm, retPtr, address, fargs[0], a, b, c, d, e, f, g, h, i, j);\n            if (res == false) {\n                throw new EvalError('Execution failed');\n            }\n            return ptr(retPtr.readRword());\n        }\n        return _call.apply(null, fargs[1]);\n    }\n\n    /**\n     * Call a function by its address (or through a Frida ``NativePointer``).\n     * QBDI will allocate his one stack to run, while the instrumented code will\n     * use the top of the current stack.\n     *\n     * Arguments can be provided, but their types need to be compatible\n     * with the ``.toRword()`` interface (like ``NativePointer`` or ``UInt64``).\n     *\n     * Example:\n     *       >>> var vm = new VM();\n     *       >>> var state = vm.getGPRState();\n     *       >>> var aFunction = Module.findGlobalExportByName(\"Secret\");\n     *       >>> vm.addInstrumentedModuleFromAddr(aFunction);\n     *       >>> vm.switchStackAndCall(aFunction, [42]);\n     *\n     * @param {String|Number|NativePointer}           address function address (or Frida ``NativePointer``).\n     * @param {StringArray|NumberArray} [args]  optional list of arguments\n     * @param {String|Number}           [stackSize] stack size for the engine.\n     */\n    switchStackAndCall(address, args, stackSize) {\n        if (stackSize === null || stackSize === undefined) {\n            stackSize = 0x20000;\n        }\n        address = address.toRword();\n        var fargs = this._formatVAArgs(args);\n        var vm = this.#vm;\n        // Use this weird construction to work around a bug in the duktape runtime\n        var _scall = function (a, b, c, d, e, f, g, h, i, j) {\n            var retPtr = Memory.alloc(Process.pointerSize);\n            var res = QBDI_C.switchStackAndCall(vm, retPtr, address, stackSize, fargs[0], a, b, c, d, e, f, g, h, i, j);\n            if (res == false) {\n                throw new EvalError('Execution failed');\n            }\n            return ptr(retPtr.readRword());\n        }\n        return _scall.apply(null, fargs[1]);\n    }\n\n    ////////////////////\n    // private method //\n    ////////////////////\n\n    _parseStructDesc(ptr) {\n        var desc = {};\n        desc.size = ptr.readU32();\n        ptr = ptr.add(4);\n        desc.items = ptr.readU32();\n        ptr = ptr.add(4);\n        desc.offsets = [];\n        for (var i = 0; i < desc.items; i++) {\n            var offset = ptr.readU32();\n            ptr = ptr.add(4);\n            desc.offsets.push(offset);\n        }\n        Object.freeze(desc);\n        return desc;\n    }\n\n    _initVM() {\n        var vmPtr = Memory.alloc(Process.pointerSize);\n        QBDI_C.initVM(vmPtr, NULL, NULL, 0);\n        return vmPtr.readPointer();\n    }\n\n    _terminateVM(v) {\n        QBDI_C.terminateVM(v);\n    }\n\n\n    // Retain (~reference) a user data object when an instrumentation is added.\n    //\n    // If a ``NativePointer`` is given, it will be used as raw user data and the\n    // object will not be retained.\n    _retainUserData(data, fn) {\n        var dataPtr = ptr(\"0\");\n        var managed = false;\n        if (data !== null && data !== undefined) {\n            this.#userDataPointer += 1;\n            dataPtr = dataPtr.add(this.#userDataPointer);\n            managed = true;\n        }\n        var iid = fn(dataPtr);\n        if (managed) {\n            this.#userDataPtrMap[dataPtr] = data;\n            this.#userDataIIdMap[iid] = dataPtr;\n        }\n        return iid;\n    }\n\n    _retainUserDataForInstrRuleCB(data, fn) {\n        this.#userDataPointer += 1;\n        var dataPtr = ptr(\"0\").add(this.#userDataPointer);\n\n        var iid = fn(dataPtr);\n\n        this.#userDataPtrMap[dataPtr] = { userdata: data, id: iid };\n        this.#userDataIIdMap[iid] = [dataPtr];\n        return iid;\n    }\n\n    _retainUserDataForInstrRuleCB2(data, id) {\n        if (data !== null && data !== undefined) {\n            this.#userDataPointer += 1;\n            var dataPtr = ptr(\"0\").add(this.#userDataPointer);\n\n            this.#userDataPtrMap[dataPtr] = data;\n            this.#userDataIIdMap[id].push(dataPtr);\n            return dataPtr;\n        } else {\n            return ptr(\"0\");\n        }\n    }\n\n    // Retrieve a user data object from its ``NativePointer`` reference.\n    // If pointer is NULL or no data object is found, the ``NativePointer``\n    // object will be returned.\n    _getUserData(dataPtr) {\n        var data = dataPtr;\n        if (!data.isNull()) {\n            var d = this.#userDataPtrMap[dataPtr];\n            if (d !== undefined) {\n                return d;\n            }\n        }\n        return undefined;\n    }\n\n    // Release references to a user data object using the correponding\n    // instrumentation id.\n    _releaseUserData(id) {\n        var dataPtr = this.#userDataIIdMap[id];\n        if (dataPtr !== undefined) {\n            if (Array.isArray(dataPtr)) {\n                for (var i = 0; i < dataPtr.length; i++) {\n                    delete this.#userDataPtrMap[dataPtr[i]];\n                }\n            } else {\n                delete this.#userDataPtrMap[dataPtr];\n            }\n            delete this.#userDataIIdMap[id];\n        }\n    }\n\n    // Release all references to user data objects.\n    _releaseAllUserData() {\n        this.#userDataPtrMap = {};\n        this.#userDataIIdMap = {};\n        this.#userDataPointer = 0;\n    }\n\n    _formatVAArgs(args) {\n        if (args === undefined) {\n            args = [];\n        }\n        var argsCnt = args.length;\n        // We are limited to 10 arguments for now\n        var fargs = new Array(10);\n        var fargsCnt = fargs.length\n        for (var i = 0; i < fargsCnt; i++) {\n            if (i < argsCnt) {\n                fargs[i] = args[i].toRword();\n            } else {\n                fargs[i] = 0;\n            }\n        }\n        return [argsCnt, fargs];\n    }\n\n\n    /**\n     * @typedef {Object} MemoryAccess\n     * @property {number} instAddress\n     * @property {number} accessAddress\n     * @property {number} value\n     * @property {number} size\n     * @property {number} type\n     * @property {number} flags\n     */\n\n    /**\n     * @param {*} ptr\n     * @returns {MemoryAccess}\n     */\n    _parseMemoryAccess(ptr) {\n        var access = {};\n        var p = ptr.add(this.#instAnalysisStructDesc.offsets[0]);\n        access.instAddress = p.readRword();\n        p = ptr.add(this.#memoryAccessDesc.offsets[1]);\n        access.accessAddress = p.readRword();\n        p = ptr.add(this.#memoryAccessDesc.offsets[2]);\n        access.value = p.readRword();\n        p = ptr.add(this.#memoryAccessDesc.offsets[3]);\n        access.size = p.readU16();\n        p = ptr.add(this.#memoryAccessDesc.offsets[4]);\n        access.type = p.readU8();\n        p = ptr.add(this.#memoryAccessDesc.offsets[5]);\n        access.flags = p.readU8();\n        Object.freeze(access);\n        return access;\n    }\n\n    _getMemoryAccess(f) {\n        var accesses = [];\n        var sizePtr = Memory.alloc(4);\n        var accessPtr = f(this.#vm, sizePtr);\n        if (accessPtr.isNull()) {\n            return [];\n        }\n        var cnt = sizePtr.readU32();\n        var sSize = this.#memoryAccessDesc.size;\n        var p = accessPtr;\n        for (var i = 0; i < cnt; i++) {\n            var access = this._parseMemoryAccess(p);\n            accesses.push(access);\n            p = p.add(sSize);\n        }\n        System.free(accessPtr);\n        return accesses;\n    }\n\n    /**\n     * @typedef {Object} VMState\n     * @property {number} event\n     * @property {number} sequenceStart\n     * @property {number} sequenceEnd\n     * @property {number} basicBlockStart\n     * @property {number} basicBlockEnd\n     * @property {number} lastSignal\n     */\n\n    /**\n     * @param {*} ptr\n     * @returns {VMState}\n     */\n    _parseVMState(ptr) {\n        var state = {};\n        var p = ptr.add(this.#instAnalysisStructDesc.offsets[0]);\n        state.event = p.readU8();\n        p = ptr.add(this.#vmStateStructDesc.offsets[1]);\n        state.sequenceStart = p.readRword();\n        p = ptr.add(this.#vmStateStructDesc.offsets[2]);\n        state.sequenceEnd = p.readRword();\n        p = ptr.add(this.#vmStateStructDesc.offsets[3]);\n        state.basicBlockStart = p.readRword();\n        p = ptr.add(this.#vmStateStructDesc.offsets[4]);\n        state.basicBlockEnd = p.readRword();\n        p = ptr.add(this.#vmStateStructDesc.offsets[5]);\n        state.lastSignal = p.readRword();\n        Object.freeze(state);\n        return state;\n    }\n\n    /**\n     * @typedef {Object} OperandAnalysis\n     * @property {number} type\n     * @property {number} flag\n     * @property {number} value\n     * @property {number} size\n     * @property {number} regOff\n     * @property {number} regCtxIdx\n     * @property {string|null} regName\n     * @property {number} regAccess\n     */\n\n    /**\n     * @param {*} ptr\n     * @returns {OperandAnalysis}\n     */\n    _parseOperandAnalysis(ptr) {\n        var analysis = {};\n        var p = ptr.add(this.#instAnalysisStructDesc.offsets[0]);\n        analysis.type = p.readU32();\n        p = ptr.add(this.#operandAnalysisStructDesc.offsets[1]);\n        analysis.flag = p.readU8();\n        p = ptr.add(this.#operandAnalysisStructDesc.offsets[2]);\n        analysis.value = p.readSword();\n        p = ptr.add(this.#operandAnalysisStructDesc.offsets[3]);\n        analysis.size = p.readU8();\n        p = ptr.add(this.#operandAnalysisStructDesc.offsets[4]);\n        analysis.regOff = p.readU8();\n        p = ptr.add(this.#operandAnalysisStructDesc.offsets[5]);\n        analysis.regCtxIdx = p.readS16();\n        p = ptr.add(this.#operandAnalysisStructDesc.offsets[6]);\n        var regNamePtr = p.readPointer();\n        if (regNamePtr.isNull()) {\n            analysis.regName = undefined;\n        } else {\n            analysis.regName = regNamePtr.readCString();\n        }\n        p = ptr.add(this.#operandAnalysisStructDesc.offsets[7]);\n        analysis.regAccess = p.readU8();\n        Object.freeze(analysis);\n        return analysis;\n    }\n\n    /**\n     * @typedef {Object} InstAnalysis\n     * @property {string|null} mnemonic\n     * @property {string|null} disassembly\n     * @property {number} address\n     * @property {number} instSize\n     * @property {boolean} affectControlFlow\n     * @property {boolean} isBranch\n     * @property {boolean} isCall\n     * @property {boolean} isReturn\n     * @property {boolean} isCompare\n     * @property {boolean} isPredicable\n     * @property {boolean} isMoveImm\n     * @property {boolean} mayLoad\n     * @property {boolean} mayStore\n     * @property {number} loadSize\n     * @property {number} storeSize\n     * @property {number} condition\n     * @property {number} flagsAccess\n     * @property {Array<OperandAnalysis>} operands\n     * @property {string|null} symbolName\n     * @property {string|null} symbol\n     * @property {number} symbolOffset\n     * @property {string|null} moduleName\n     * @property {string|null} module\n     * @property {string|null} cpuMode\n     * @property {number} patchSize\n     * @property {number} patchInstOffset\n     * @property {number} patchInstSize\n     * @property {number} analysisType\n     */\n\n    /**\n     * @param {*} ptr\n     * @returns {InstAnalysis}\n     */\n    _parseInstAnalysis(ptr) {\n        var analysis = {};\n        var p = ptr.add(this.#instAnalysisStructDesc.offsets[27]);\n        var analysisType = p.readU16();\n\n        if ((analysisType & AnalysisType.ANALYSIS_INSTRUCTION) != 0) {\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[0]);\n          analysis.mnemonic = p.readPointer().readCString();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[2]);\n          analysis.address = p.readRword();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[3]);\n          analysis.instSize = p.readU32();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[4]);\n          analysis.affectControlFlow = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[5]);\n          analysis.isBranch = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[6]);\n          analysis.isCall = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[7]);\n          analysis.isReturn = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[8]);\n          analysis.isCompare = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[9]);\n          analysis.isPredicable = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[10]);\n          analysis.isMoveImm = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[11]);\n          analysis.mayLoad = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[12]);\n          analysis.mayStore = p.readU8() == true;\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[13]);\n          analysis.loadSize = p.readU32();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[14]);\n          analysis.storeSize = p.readU32();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[15]);\n          analysis.condition = p.readU8();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[22]);\n          analysis.cpuMode = p.readU8();\n        }\n        if ((analysisType & AnalysisType.ANALYSIS_DISASSEMBLY) != 0) {\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[1]);\n          analysis.disassembly = p.readPointer().readCString();\n        }\n        if ((analysisType & AnalysisType.ANALYSIS_OPERANDS) != 0) {\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[16]);\n          analysis.flagsAccess = p.readU8();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[17]);\n          var numOperands = p.readU8();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[18]);\n          var operandsPtr = p.readPointer();\n          analysis.operands = new Array(numOperands);\n          for (var i = 0; i < numOperands; i++) {\n              analysis.operands[i] = this._parseOperandAnalysis(operandsPtr);\n              operandsPtr = operandsPtr.add(this.#operandAnalysisStructDesc.size);\n          }\n        }\n        if ((analysisType & AnalysisType.ANALYSIS_SYMBOL) != 0) {\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[19]);\n          var symbolPtr = p.readPointer();\n          if (!symbolPtr.isNull()) {\n              analysis.symbolName = symbolPtr.readCString();\n          } else {\n              analysis.symbolName = \"\";\n          }\n          analysis.symbol = analysis.symbolName; // deprecated Name\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[20]);\n          analysis.symbolOffset = p.readU32();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[21]);\n          var modulePtr = p.readPointer();\n          if (!modulePtr.isNull()) {\n              analysis.moduleName = modulePtr.readCString();\n          } else {\n              analysis.moduleName = \"\";\n          }\n          analysis.module = analysis.moduleName; // deprecated Name\n        }\n        if ((analysisType & AnalysisType.ANALYSIS_JIT) != 0) {\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[23]);\n          analysis.patchAddress = p.readRword();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[24]);\n          analysis.patchSize = p.readU16();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[25]);\n          analysis.patchInstOffset = p.readU16();\n          p = ptr.add(this.#instAnalysisStructDesc.offsets[26]);\n          analysis.patchInstSize = p.readU16();\n        }\n\n        Object.freeze(analysis);\n        return analysis;\n    }\n\n};\n\n"
  },
  {
    "path": "tools/pyqbdi/CMakeLists.txt",
    "content": "if(QBDI_ARCH_X86\n   OR QBDI_ARCH_X86_64\n   OR QBDI_ARCH_AARCH64\n   OR QBDI_ARCH_ARM)\n\n  if(DEFINED QBDI_TOOLS_PYQBDI_TARGET_PYTHON_VERSION)\n    find_package(\n      Python3 \"${QBDI_TOOLS_PYQBDI_TARGET_PYTHON_VERSION}\" EXACT REQUIRED\n      COMPONENTS Interpreter Development.Module\n      OPTIONAL_COMPONENTS Development.Embed)\n  else()\n    find_package(\n      Python3 REQUIRED\n      COMPONENTS Interpreter Development.Module\n      OPTIONAL_COMPONENTS Development.Embed)\n  endif()\n\n  if(NOT Python3_FOUND)\n    message(FATAL_ERROR \"Python3 not found\")\n  endif()\n\n  # in manylinux image, python doesn't have a library\n  # If Python3::Development is not found, try to found the include with the executable\n  if(NOT Python3_Development_FOUND AND \"${Python3_INCLUDE_DIRS}\" STREQUAL \"\")\n    execute_process(\n      COMMAND \"${Python3_EXECUTABLE}\" -c\n              \"from sysconfig import get_paths; print(get_paths()['include'])\"\n      OUTPUT_VARIABLE Python3_INCLUDE_DIRS\n      OUTPUT_STRIP_TRAILING_WHITESPACE)\n    if(\"${Python3_INCLUDE_DIRS}\" STREQUAL \"\")\n      message(FATAL_ERROR \"Python3_INCLUDE_DIRS not found\")\n    endif()\n  endif()\n\n  execute_process(\n    COMMAND \"${Python3_EXECUTABLE}\" -c\n            \"import platform; print(platform.architecture()[0])\"\n    OUTPUT_VARIABLE PYTHON_BIT\n    OUTPUT_STRIP_TRAILING_WHITESPACE)\n\n  message(STATUS \"Python lib:     ${Python3_LIBRARIES}\")\n  message(STATUS \"Python include: ${Python3_INCLUDE_DIRS}\")\n  message(STATUS \"Python interpreter: ${Python3_EXECUTABLE}\")\n  message(STATUS \"Python ${PYTHON_BIT}\")\n\n  # verify we compile pyqbdi tools with the good version of python\n  if((${PYTHON_BIT} STREQUAL \"64bit\") AND NOT (QBDI_ARCH_X86_64\n                                               OR QBDI_ARCH_AARCH64))\n    message(\n      FATAL_ERROR\n        \"PyQBDI with Python ${PYTHON_BIT} is not compatible with the architecture ${QBDI_ARCH}\"\n    )\n  elseif((${PYTHON_BIT} STREQUAL \"32bit\") AND NOT (QBDI_ARCH_X86\n                                                   OR QBDI_ARCH_ARM))\n    message(\n      FATAL_ERROR\n        \"PyQBDI with Python ${PYTHON_BIT} is not compatible with the architecture ${QBDI_ARCH}\"\n    )\n  endif()\n\n  add_library(pyqbdi_utils INTERFACE)\n  add_dependencies(pyqbdi_utils pyqbdi_pybind11)\n\n  add_library(pyqbdi_module INTERFACE)\n  add_dependencies(pyqbdi_module pyqbdi_pybind11)\n\n  if(QBDI_PLATFORM_WINDOWS)\n    target_compile_options(pyqbdi_utils INTERFACE \"/GR\" \"/EHsc\")\n    target_compile_options(pyqbdi_module INTERFACE \"/GR\" \"/EHsc\")\n  else()\n    target_compile_options(pyqbdi_utils INTERFACE \"-frtti\")\n    target_compile_options(pyqbdi_module INTERFACE \"-frtti\")\n  endif()\n\n  include(\"${CMAKE_CURRENT_SOURCE_DIR}/utils/CMakeLists.txt\")\n  include(\"${CMAKE_CURRENT_SOURCE_DIR}/binding/CMakeLists.txt\")\n\n  ##\n  # Shared library with QBDIPreload (if available)\n  ##\n  set(PYQBDI_SRC_MODULE \"${CMAKE_CURRENT_SOURCE_DIR}/pyqbdi_module.cpp\")\n  pybind11_add_module(pyqbdi ${PYQBDI_SRC_MODULE})\n  target_include_directories(\n    pyqbdi PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>\n                   ${CMAKE_CURRENT_SOURCE_DIR} ${Python3_INCLUDE_DIRS})\n\n  target_link_libraries(pyqbdi PRIVATE pyqbdi_module pyqbdi_utils QBDI_static)\n  set_target_properties(pyqbdi PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED\n                                                          ON)\n\n  set_target_properties(pyqbdi PROPERTIES PREFIX \"\")\n\n  if(QBDI_PLATFORM_MACOS)\n    set_target_properties(pyqbdi PROPERTIES LINK_FLAGS\n                                            \"-undefined dynamic_lookup\")\n  elseif(QBDI_PLATFORM_LINUX)\n    set_target_properties(pyqbdi PROPERTIES LINK_FLAGS \"-s\")\n  elseif(QBDI_PLATFORM_WINDOWS)\n    set_target_properties(pyqbdi PROPERTIES SUFFIX \".pyd\")\n  endif()\n\n  if(QBDI_TOOLS_QBDIPRELOAD)\n    # Due to the use of embeded interpreter in PyQBDIPreload, we should like\n    # it against the python library. However, in manylinux image,\n    # lipython3.XX.so is not available. The script pyqbdipreload.py will search\n    # for this library at the runtime, so the lipython3.XX.so is not available\n    # at build time, just omit it.\n    if(Python3_Development.Embed_FOUND)\n      pybind11_add_module(pyqbdipreloadlib SHARED\n                          \"${CMAKE_CURRENT_SOURCE_DIR}/preload.cpp\")\n    else()\n      pybind11_add_module(pyqbdipreloadlib\n                          \"${CMAKE_CURRENT_SOURCE_DIR}/preload.cpp\")\n    endif()\n\n    target_include_directories(\n      pyqbdipreloadlib\n      PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>\n              \"${CMAKE_CURRENT_SOURCE_DIR}\"\n              \"${CMAKE_CURRENT_SOURCE_DIR}/../QBDIPreload/include\"\n              ${Python3_INCLUDE_DIRS})\n\n    target_link_libraries(pyqbdipreloadlib PRIVATE pyqbdi_module pyqbdi_utils\n                                                   QBDIPreload QBDI_static)\n    set_target_properties(pyqbdipreloadlib PROPERTIES CXX_STANDARD 14\n                                                      CXX_STANDARD_REQUIRED ON)\n    set_target_properties(pyqbdipreloadlib PROPERTIES PREFIX \"\")\n\n    if(QBDI_PLATFORM_MACOS)\n      set_target_properties(pyqbdipreloadlib\n                            PROPERTIES LINK_FLAGS \"-undefined dynamic_lookup\")\n      # fix import library of pyqbdipreloadlib:\n      # - remove all rpath\n      # - change id\n      # - replace Python lib by libpythonX.Y.dylib\n      # - resign\n      add_custom_command(\n        TARGET pyqbdipreloadlib\n        POST_BUILD\n        COMMAND\n          \"${Python3_EXECUTABLE}\"\n          \"${CMAKE_CURRENT_SOURCE_DIR}/fix_preload_lib_macos.py\"\n          $<TARGET_FILE:pyqbdipreloadlib>)\n    elseif(QBDI_PLATFORM_LINUX)\n      set_target_properties(pyqbdipreloadlib PROPERTIES LINK_FLAGS \"-s\")\n    endif()\n\n  endif()\n\n  if(PYQBDI_OUTPUT_DIRECTORY)\n    message(STATUS \"export PYQBDI to ${PYQBDI_OUTPUT_DIRECTORY}/\")\n    add_custom_command(\n      TARGET pyqbdi\n      POST_BUILD\n      COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pyqbdi>\n              ${PYQBDI_OUTPUT_DIRECTORY}/$<TARGET_FILE_NAME:pyqbdi>)\n    if(QBDI_TOOLS_QBDIPRELOAD)\n      add_custom_command(\n        TARGET pyqbdipreloadlib\n        POST_BUILD\n        COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pyqbdipreloadlib>\n                ${PYQBDI_OUTPUT_DIRECTORY}/$<TARGET_FILE_NAME:pyqbdipreloadlib>\n        COMMAND\n          ${CMAKE_COMMAND} -E copy\n          \"${CMAKE_CURRENT_SOURCE_DIR}/pyqbdipreload.py\"\n          \"${PYQBDI_OUTPUT_DIRECTORY}/pyqbdipreload.py\")\n    endif()\n\n    # Note Windows : also see tools/QBDIWinPreloader/CMakeLists.txt for WinPreload in wheel package\n  endif()\n\n  ##\n  # Library for doc\n  ##\n  add_custom_command(\n    TARGET pyqbdi\n    POST_BUILD\n    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pyqbdi>\n            ${PROJECT_BINARY_DIR}/docs/$<TARGET_FILE_NAME:pyqbdi>)\n\nendif()\n"
  },
  {
    "path": "tools/pyqbdi/binding/AARCH64/CMakeLists.txt",
    "content": "target_sources(\n  pyqbdi_module INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/State_AARCH64.cpp\"\n                          \"${CMAKE_CURRENT_LIST_DIR}/Options_AARCH64.cpp\")\n"
  },
  {
    "path": "tools/pyqbdi/binding/AARCH64/Options_AARCH64.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Enum.hpp\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Options(py::module_ &m) {\n\n  enum_int_flag_<Options>(m, \"Options\", \"VM options\", py::arithmetic())\n      .value(\"NO_OPT\", Options::NO_OPT, \"Default value\")\n      .value(\"OPT_DISABLE_FPR\", Options::OPT_DISABLE_FPR,\n             \"Disable all operation on FPU (SSE, AVX, SIMD). May break the \"\n             \"execution if the target use the FPU.\")\n      .value(\"OPT_DISABLE_OPTIONAL_FPR\", Options::OPT_DISABLE_OPTIONAL_FPR,\n             \"Disable context switch optimisation when the target execblock \"\n             \"doesn't used FPR\")\n      .value(\"OPT_DISABLE_MEMORYACCESS_VALUE\",\n             Options::OPT_DISABLE_MEMORYACCESS_VALUE,\n             \"Don't load memory access value\")\n      .value(\"OPT_DISABLE_ERRNO_BACKUP\", Options::OPT_DISABLE_ERRNO_BACKUP,\n             \"Don't save and restore errno\")\n      .value(\"OPT_DISABLE_LOCAL_MONITOR\", Options::OPT_DISABLE_LOCAL_MONITOR,\n             \"Disable the local monitor for instruction like stxr\")\n      .value(\"OPT_BYPASS_PAUTH\", Options::OPT_BYPASS_PAUTH,\n             \"Disable pointeur authentication\")\n      .value(\"OPT_ENABLE_BTI\", Options::OPT_ENABLE_BTI,\n             \"Enable BTI on instrumented code\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/AARCH64/State_AARCH64.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid hexify_register(std::ostringstream &out, __int128 v) {\n  out << std::setw(32)\n      << (static_cast<uint64_t>(v >> 64) & 0xffffffffffffffffull);\n  out << std::setw(32) << (static_cast<uint64_t>(v) & 0xffffffffffffffffull);\n}\n\nvoid init_binding_State(py::module_ &m) {\n\n  py::class_<FPRState>(m, \"FPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"fpcr\", &FPRState::fpcr, \"FPCS\")\n      .def_readwrite(\"fpsr\", &FPRState::fpsr, \"FPSR\")\n      .def_property(\n          \"v0\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v0, sizeof(t.v0));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v0, sizeof(t.v0), 0);\n          },\n          \"v0\")\n      .def_property(\n          \"v1\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v1, sizeof(t.v1));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v1, sizeof(t.v1), 0);\n          },\n          \"v1\")\n      .def_property(\n          \"v2\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v2, sizeof(t.v2));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v2, sizeof(t.v2), 0);\n          },\n          \"v2\")\n      .def_property(\n          \"v3\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v3, sizeof(t.v3));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v3, sizeof(t.v3), 0);\n          },\n          \"v3\")\n      .def_property(\n          \"v4\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v4, sizeof(t.v4));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v4, sizeof(t.v4), 0);\n          },\n          \"v4\")\n      .def_property(\n          \"v5\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v5, sizeof(t.v5));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v5, sizeof(t.v5), 0);\n          },\n          \"v5\")\n      .def_property(\n          \"v6\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v6, sizeof(t.v6));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v6, sizeof(t.v6), 0);\n          },\n          \"v6\")\n      .def_property(\n          \"v7\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v7, sizeof(t.v7));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v7, sizeof(t.v7), 0);\n          },\n          \"v7\")\n      .def_property(\n          \"v8\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v8, sizeof(t.v8));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v8, sizeof(t.v8), 0);\n          },\n          \"v8\")\n      .def_property(\n          \"v9\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v9, sizeof(t.v9));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v9, sizeof(t.v9), 0);\n          },\n          \"v9\")\n      .def_property(\n          \"v10\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v10, sizeof(t.v10));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v10, sizeof(t.v10), 0);\n          },\n          \"v10\")\n      .def_property(\n          \"v11\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v11, sizeof(t.v11));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v11, sizeof(t.v11), 0);\n          },\n          \"v11\")\n      .def_property(\n          \"v12\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v12, sizeof(t.v12));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v12, sizeof(t.v12), 0);\n          },\n          \"v12\")\n      .def_property(\n          \"v13\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v13, sizeof(t.v13));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v13, sizeof(t.v13), 0);\n          },\n          \"v13\")\n      .def_property(\n          \"v14\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v14, sizeof(t.v14));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v14, sizeof(t.v14), 0);\n          },\n          \"v14\")\n      .def_property(\n          \"v15\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v15, sizeof(t.v15));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v15, sizeof(t.v15), 0);\n          },\n          \"v15\")\n      .def_property(\n          \"v16\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v16, sizeof(t.v16));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v16, sizeof(t.v16), 0);\n          },\n          \"v16\")\n      .def_property(\n          \"v17\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v17, sizeof(t.v17));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v17, sizeof(t.v17), 0);\n          },\n          \"v17\")\n      .def_property(\n          \"v18\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v18, sizeof(t.v18));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v18, sizeof(t.v18), 0);\n          },\n          \"v18\")\n      .def_property(\n          \"v19\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v19, sizeof(t.v19));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v19, sizeof(t.v19), 0);\n          },\n          \"v19\")\n      .def_property(\n          \"v20\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v20, sizeof(t.v20));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v20, sizeof(t.v20), 0);\n          },\n          \"v20\")\n      .def_property(\n          \"v21\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v21, sizeof(t.v21));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v21, sizeof(t.v21), 0);\n          },\n          \"v21\")\n      .def_property(\n          \"v22\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v22, sizeof(t.v22));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v22, sizeof(t.v22), 0);\n          },\n          \"v22\")\n      .def_property(\n          \"v23\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v23, sizeof(t.v23));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v23, sizeof(t.v23), 0);\n          },\n          \"v23\")\n      .def_property(\n          \"v24\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v24, sizeof(t.v24));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v24, sizeof(t.v24), 0);\n          },\n          \"v24\")\n      .def_property(\n          \"v25\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v25, sizeof(t.v25));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v25, sizeof(t.v25), 0);\n          },\n          \"v25\")\n      .def_property(\n          \"v26\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v26, sizeof(t.v26));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v26, sizeof(t.v26), 0);\n          },\n          \"v26\")\n      .def_property(\n          \"v27\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v27, sizeof(t.v27));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v27, sizeof(t.v27), 0);\n          },\n          \"v27\")\n      .def_property(\n          \"v28\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v28, sizeof(t.v28));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v28, sizeof(t.v28), 0);\n          },\n          \"v28\")\n      .def_property(\n          \"v29\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v29, sizeof(t.v29));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v29, sizeof(t.v29), 0);\n          },\n          \"v29\")\n      .def_property(\n          \"v30\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v30, sizeof(t.v30));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v30, sizeof(t.v30), 0);\n          },\n          \"v30\")\n      .def_property(\n          \"v31\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.v31, sizeof(t.v31));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.v31, sizeof(t.v31), 0);\n          },\n          \"v31\")\n      .def(\"__str__\",\n           [](const FPRState &obj) {\n             std::ostringstream oss;\n             oss << std::hex << std::setfill('0')\n                 << \"=== FPRState begin ===\" << std::endl\n                 << \"fpcr  : 0x\" << std::setw(sizeof(rword) * 2) << obj.fpcr\n                 << std::endl\n                 << \"fpsr  : 0x\" << std::setw(sizeof(rword) * 2) << obj.fpsr\n                 << std::endl\n                 << \"v0  : 0x\";\n             hexify_register(oss, obj.v0);\n             oss << std::endl << \"v1  : 0x\";\n             hexify_register(oss, obj.v1);\n             oss << std::endl << \"v2  : 0x\";\n             hexify_register(oss, obj.v2);\n             oss << std::endl << \"v3  : 0x\";\n             hexify_register(oss, obj.v3);\n             oss << std::endl << \"v4  : 0x\";\n             hexify_register(oss, obj.v4);\n             oss << std::endl << \"v5  : 0x\";\n             hexify_register(oss, obj.v5);\n             oss << std::endl << \"v6  : 0x\";\n             hexify_register(oss, obj.v6);\n             oss << std::endl << \"v7  : 0x\";\n             hexify_register(oss, obj.v7);\n             oss << std::endl << \"v8  : 0x\";\n             hexify_register(oss, obj.v8);\n             oss << std::endl << \"v9  : 0x\";\n             hexify_register(oss, obj.v9);\n             oss << std::endl << \"v10 : 0x\";\n             hexify_register(oss, obj.v10);\n             oss << std::endl << \"v11 : 0x\";\n             hexify_register(oss, obj.v11);\n             oss << std::endl << \"v12 : 0x\";\n             hexify_register(oss, obj.v12);\n             oss << std::endl << \"v13 : 0x\";\n             hexify_register(oss, obj.v13);\n             oss << std::endl << \"v14 : 0x\";\n             hexify_register(oss, obj.v14);\n             oss << std::endl << \"v15 : 0x\";\n             hexify_register(oss, obj.v15);\n             oss << std::endl << \"v16 : 0x\";\n             hexify_register(oss, obj.v16);\n             oss << std::endl << \"v17 : 0x\";\n             hexify_register(oss, obj.v17);\n             oss << std::endl << \"v18 : 0x\";\n             hexify_register(oss, obj.v18);\n             oss << std::endl << \"v19 : 0x\";\n             hexify_register(oss, obj.v19);\n             oss << std::endl << \"v20 : 0x\";\n             hexify_register(oss, obj.v20);\n             oss << std::endl << \"v21 : 0x\";\n             hexify_register(oss, obj.v21);\n             oss << std::endl << \"v22 : 0x\";\n             hexify_register(oss, obj.v22);\n             oss << std::endl << \"v23 : 0x\";\n             hexify_register(oss, obj.v23);\n             oss << std::endl << \"v24 : 0x\";\n             hexify_register(oss, obj.v24);\n             oss << std::endl << \"v25 : 0x\";\n             hexify_register(oss, obj.v25);\n             oss << std::endl << \"v26 : 0x\";\n             hexify_register(oss, obj.v26);\n             oss << std::endl << \"v27 : 0x\";\n             hexify_register(oss, obj.v27);\n             oss << std::endl << \"v28 : 0x\";\n             hexify_register(oss, obj.v28);\n             oss << std::endl << \"v29 : 0x\";\n             hexify_register(oss, obj.v29);\n             oss << std::endl << \"v30 : 0x\";\n             hexify_register(oss, obj.v30);\n             oss << std::endl << \"v31 : 0x\";\n             hexify_register(oss, obj.v31);\n\n             oss << std::endl << \"=== FPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\"__copy__\", [](const FPRState &state) -> FPRState { return state; })\n      .def(py::pickle(\n          [](const FPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"AARCH64\", py::bytes(reinterpret_cast<const char *>(&state),\n                                     sizeof(FPRState)));\n          },\n          [](py::tuple t) -> FPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"AARCH64\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"AARCH64\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(FPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(FPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            FPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(FPRState));\n            return newState;\n\n          }));\n\n  m.attr(\"REG_RETURN\") = REG_RETURN;\n  m.attr(\"AVAILABLE_GPR\") = AVAILABLE_GPR;\n  m.attr(\"REG_BP\") = REG_BP;\n  m.attr(\"REG_SP\") = REG_SP;\n  m.attr(\"REG_PC\") = REG_PC;\n  m.attr(\"NUM_GPR\") = NUM_GPR;\n  m.attr(\"REG_LR\") = REG_LR;\n  m.attr(\"REG_FLAG\") = REG_FLAG;\n\n  py::class_<GPRState>(m, \"GPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"x0\", &GPRState::x0)\n      .def_readwrite(\"x1\", &GPRState::x1)\n      .def_readwrite(\"x2\", &GPRState::x2)\n      .def_readwrite(\"x3\", &GPRState::x3)\n      .def_readwrite(\"x4\", &GPRState::x4)\n      .def_readwrite(\"x5\", &GPRState::x5)\n      .def_readwrite(\"x6\", &GPRState::x6)\n      .def_readwrite(\"x7\", &GPRState::x7)\n      .def_readwrite(\"x8\", &GPRState::x8)\n      .def_readwrite(\"x9\", &GPRState::x9)\n      .def_readwrite(\"x10\", &GPRState::x10)\n      .def_readwrite(\"x11\", &GPRState::x11)\n      .def_readwrite(\"x12\", &GPRState::x12)\n      .def_readwrite(\"x13\", &GPRState::x13)\n      .def_readwrite(\"x14\", &GPRState::x14)\n      .def_readwrite(\"x15\", &GPRState::x15)\n      .def_readwrite(\"x16\", &GPRState::x16)\n      .def_readwrite(\"x17\", &GPRState::x17)\n      .def_readwrite(\"x18\", &GPRState::x18)\n      .def_readwrite(\"x19\", &GPRState::x19)\n      .def_readwrite(\"x20\", &GPRState::x20)\n      .def_readwrite(\"x21\", &GPRState::x21)\n      .def_readwrite(\"x22\", &GPRState::x22)\n      .def_readwrite(\"x23\", &GPRState::x23)\n      .def_readwrite(\"x24\", &GPRState::x24)\n      .def_readwrite(\"x25\", &GPRState::x25)\n      .def_readwrite(\"x26\", &GPRState::x26)\n      .def_readwrite(\"x27\", &GPRState::x27)\n      .def_readwrite(\"x28\", &GPRState::x28)\n      .def_readwrite(\"x29\", &GPRState::x29)\n      .def_readwrite(\"fp\", &GPRState::x29, \"shadow of x29\")\n      .def_readwrite(\"lr\", &GPRState::lr)\n      .def_readwrite(\"sp\", &GPRState::sp)\n      .def_readwrite(\"nzcv\", &GPRState::nzcv)\n      .def_readwrite(\"pc\", &GPRState::pc)\n      .def_property(\n          \"localMonitor_addr\",\n          [](const GPRState &t) { return py::int_(t.localMonitor.addr); },\n          [](GPRState &t, py::int_ v) { t.localMonitor.addr = v; },\n          \"localMonitor : exclusive base address\")\n      .def_property(\n          \"localMonitor_enable\",\n          [](const GPRState &t) {\n            return py::bool_(t.localMonitor.enable != 0);\n          },\n          [](GPRState &t, py::bool_ v) { t.localMonitor.enable = v ? 1 : 0; },\n          \"localMonitor : exclusive state\")\n      // cross architecture access\n      .def_readwrite(\"REG_RETURN\", &GPRState::x0, \"shadow of x0\")\n      .def_readwrite(\"AVAILABLE_GPR\", &GPRState::x28, \"shadow of x28\")\n      .def_readwrite(\"REG_BP\", &GPRState::x29, \"shadow of x29 / fp\")\n      .def_readwrite(\"REG_SP\", &GPRState::sp, \"shadow of sp\")\n      .def_readwrite(\"REG_PC\", &GPRState::pc, \"shadow of PC\")\n      .def_readwrite(\"NUM_GPR\", &GPRState::nzcv, \"shadow of nzcv\")\n      .def_readwrite(\"REG_LR\", &GPRState::lr, \"shadow of lr\")\n      .def_readwrite(\"REG_FLAG\", &GPRState::nzcv, \"shadow of nzcv\")\n      .def(\"__str__\",\n           [](const GPRState &obj) {\n             std::ostringstream oss;\n             int r = sizeof(rword) * 2;\n             oss << std::hex << std::setfill('0')\n                 << \"=== GPRState begin ===\" << std::endl\n                 << \"x0     : 0x\" << std::setw(r) << obj.x0 << std::endl\n                 << \"x1     : 0x\" << std::setw(r) << obj.x1 << std::endl\n                 << \"x2     : 0x\" << std::setw(r) << obj.x2 << std::endl\n                 << \"x3     : 0x\" << std::setw(r) << obj.x3 << std::endl\n                 << \"x4     : 0x\" << std::setw(r) << obj.x4 << std::endl\n                 << \"x5     : 0x\" << std::setw(r) << obj.x5 << std::endl\n                 << \"x6     : 0x\" << std::setw(r) << obj.x6 << std::endl\n                 << \"x7     : 0x\" << std::setw(r) << obj.x7 << std::endl\n                 << \"x8     : 0x\" << std::setw(r) << obj.x8 << std::endl\n                 << \"x9     : 0x\" << std::setw(r) << obj.x9 << std::endl\n                 << \"x10    : 0x\" << std::setw(r) << obj.x10 << std::endl\n                 << \"x11    : 0x\" << std::setw(r) << obj.x11 << std::endl\n                 << \"x12    : 0x\" << std::setw(r) << obj.x12 << std::endl\n                 << \"x13    : 0x\" << std::setw(r) << obj.x13 << std::endl\n                 << \"x14    : 0x\" << std::setw(r) << obj.x14 << std::endl\n                 << \"x15    : 0x\" << std::setw(r) << obj.x15 << std::endl\n                 << \"x16    : 0x\" << std::setw(r) << obj.x16 << std::endl\n                 << \"x17    : 0x\" << std::setw(r) << obj.x17 << std::endl\n                 << \"x18    : 0x\" << std::setw(r) << obj.x18 << std::endl\n                 << \"x19    : 0x\" << std::setw(r) << obj.x19 << std::endl\n                 << \"x20    : 0x\" << std::setw(r) << obj.x20 << std::endl\n                 << \"x21    : 0x\" << std::setw(r) << obj.x21 << std::endl\n                 << \"x22    : 0x\" << std::setw(r) << obj.x22 << std::endl\n                 << \"x23    : 0x\" << std::setw(r) << obj.x23 << std::endl\n                 << \"x24    : 0x\" << std::setw(r) << obj.x24 << std::endl\n                 << \"x25    : 0x\" << std::setw(r) << obj.x25 << std::endl\n                 << \"x26    : 0x\" << std::setw(r) << obj.x26 << std::endl\n                 << \"x27    : 0x\" << std::setw(r) << obj.x27 << std::endl\n                 << \"x28    : 0x\" << std::setw(r) << obj.x28 << std::endl\n                 << \"x29|FP : 0x\" << std::setw(r) << obj.x29 << std::endl\n                 << \"LR     : 0x\" << std::setw(r) << obj.lr << std::endl\n                 << \"SP     : 0x\" << std::setw(r) << obj.sp << std::endl\n                 << \"nzcv   : 0x\" << std::setw(r) << obj.nzcv << std::endl\n                 << \"PC     : 0x\" << std::setw(r) << obj.pc << std::endl\n                 << \"=== GPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\n          \"__getitem__\",\n          [](const GPRState &obj, unsigned int index) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_GET(&obj, index);\n          },\n          \"Get a register like QBDI_GPR_GET\", \"index\"_a)\n      .def(\n          \"__setitem__\",\n          [](GPRState &obj, unsigned int index, rword value) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_SET(&obj, index, value);\n          },\n          \"Set a register like QBDI_GPR_SET\", \"index\"_a, \"value\"_a)\n      .def(\"__copy__\", [](const GPRState &state) -> GPRState { return state; })\n      .def(py::pickle(\n          [](const GPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"AARCH64\", py::bytes(reinterpret_cast<const char *>(&state),\n                                     sizeof(GPRState)));\n          },\n          [](py::tuple t) -> GPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"AARCH64\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"AARCH64\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(GPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(GPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            GPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(GPRState));\n            return newState;\n\n          }));\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/ARM/CMakeLists.txt",
    "content": "target_sources(\n  pyqbdi_module\n  INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/Options_ARM.cpp\"\n  INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/State_ARM.cpp\")\n"
  },
  {
    "path": "tools/pyqbdi/binding/ARM/Options_ARM.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Enum.hpp\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Options(py::module_ &m) {\n\n  enum_int_flag_<Options>(m, \"Options\", \"VM options\", py::arithmetic())\n      .value(\"NO_OPT\", Options::NO_OPT, \"Default value\")\n      .value(\"OPT_DISABLE_FPR\", Options::OPT_DISABLE_FPR,\n             \"Disable all operation on FPU (SSE, AVX, SIMD). May break the \"\n             \"execution if the target use the FPU.\")\n      .value(\"OPT_DISABLE_OPTIONAL_FPR\", Options::OPT_DISABLE_OPTIONAL_FPR,\n             \"Disable context switch optimisation when the target execblock \"\n             \"doesn't used FPR\")\n      .value(\"OPT_DISABLE_MEMORYACCESS_VALUE\",\n             Options::OPT_DISABLE_MEMORYACCESS_VALUE,\n             \"Don't load memory access value\")\n      .value(\"OPT_DISABLE_ERRNO_BACKUP\", Options::OPT_DISABLE_ERRNO_BACKUP,\n             \"Don't save and restore errno\")\n      .value(\"OPT_DISABLE_LOCAL_MONITOR\", Options::OPT_DISABLE_LOCAL_MONITOR,\n             \"Disable the local monitor for instruction like stxr\")\n      .value(\"OPT_DISABLE_D16_D31\", Options::OPT_DISABLE_D16_D31,\n             \"Disable the used of D16-D31 register\")\n      .value(\"OPT_ARMv4\", Options::OPT_ARMv4,\n             \"Change between ARM and Thumb as an ARMv4 CPU\")\n      .value(\"OPT_ARMv5T_6\", Options::OPT_ARMv5T_6,\n             \"Change between ARM and Thumb as an ARMv5T or ARMv6 CPU\")\n      .value(\"OPT_ARMv7\", Options::OPT_ARMv7,\n             \"Change between ARM and Thumb as an ARMv7 CPU (default)\")\n      .value(\"OPT_ARM_MASK\", Options::OPT_ARM_MASK, \"\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/ARM/State_ARM.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_State(py::module_ &m) {\n\n  py::class_<FPRState>(m, \"FPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"fpscr\", &FPRState::fpscr, \"FPSCR\")\n      .def_property(\n          \"s0\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[0], sizeof(t.vreg.s[0]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[0], sizeof(t.vreg.s[0]), 0);\n          },\n          \"s0\")\n      .def_property(\n          \"s1\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[1], sizeof(t.vreg.s[1]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[1], sizeof(t.vreg.s[1]), 0);\n          },\n          \"s1\")\n      .def_property(\n          \"s2\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[2], sizeof(t.vreg.s[2]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[2], sizeof(t.vreg.s[2]), 0);\n          },\n          \"s2\")\n      .def_property(\n          \"s3\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[3], sizeof(t.vreg.s[3]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[3], sizeof(t.vreg.s[3]), 0);\n          },\n          \"s3\")\n      .def_property(\n          \"s4\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[4], sizeof(t.vreg.s[4]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[4], sizeof(t.vreg.s[4]), 0);\n          },\n          \"s4\")\n      .def_property(\n          \"s5\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[5], sizeof(t.vreg.s[5]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[5], sizeof(t.vreg.s[5]), 0);\n          },\n          \"s5\")\n      .def_property(\n          \"s6\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[6], sizeof(t.vreg.s[6]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[6], sizeof(t.vreg.s[6]), 0);\n          },\n          \"s6\")\n      .def_property(\n          \"s7\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[7], sizeof(t.vreg.s[7]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[7], sizeof(t.vreg.s[7]), 0);\n          },\n          \"s7\")\n      .def_property(\n          \"s8\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[8], sizeof(t.vreg.s[8]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[8], sizeof(t.vreg.s[8]), 0);\n          },\n          \"s8\")\n      .def_property(\n          \"s9\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[9], sizeof(t.vreg.s[9]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[9], sizeof(t.vreg.s[9]), 0);\n          },\n          \"s9\")\n      .def_property(\n          \"s10\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[10], sizeof(t.vreg.s[10]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[10], sizeof(t.vreg.s[10]), 0);\n          },\n          \"s10\")\n      .def_property(\n          \"s11\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[11], sizeof(t.vreg.s[11]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[11], sizeof(t.vreg.s[11]), 0);\n          },\n          \"s11\")\n      .def_property(\n          \"s12\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[12], sizeof(t.vreg.s[12]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[12], sizeof(t.vreg.s[12]), 0);\n          },\n          \"s12\")\n      .def_property(\n          \"s13\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[13], sizeof(t.vreg.s[13]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[13], sizeof(t.vreg.s[13]), 0);\n          },\n          \"s13\")\n      .def_property(\n          \"s14\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[14], sizeof(t.vreg.s[14]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[14], sizeof(t.vreg.s[14]), 0);\n          },\n          \"s14\")\n      .def_property(\n          \"s15\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[15], sizeof(t.vreg.s[15]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[15], sizeof(t.vreg.s[15]), 0);\n          },\n          \"s15\")\n      .def_property(\n          \"s16\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[16], sizeof(t.vreg.s[16]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[16], sizeof(t.vreg.s[16]), 0);\n          },\n          \"s16\")\n      .def_property(\n          \"s17\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[17], sizeof(t.vreg.s[17]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[17], sizeof(t.vreg.s[17]), 0);\n          },\n          \"s17\")\n      .def_property(\n          \"s18\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[18], sizeof(t.vreg.s[18]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[18], sizeof(t.vreg.s[18]), 0);\n          },\n          \"s18\")\n      .def_property(\n          \"s19\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[19], sizeof(t.vreg.s[19]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[19], sizeof(t.vreg.s[19]), 0);\n          },\n          \"s19\")\n      .def_property(\n          \"s20\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[20], sizeof(t.vreg.s[20]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[20], sizeof(t.vreg.s[20]), 0);\n          },\n          \"s20\")\n      .def_property(\n          \"s21\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[21], sizeof(t.vreg.s[21]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[21], sizeof(t.vreg.s[21]), 0);\n          },\n          \"s21\")\n      .def_property(\n          \"s22\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[22], sizeof(t.vreg.s[22]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[22], sizeof(t.vreg.s[22]), 0);\n          },\n          \"s22\")\n      .def_property(\n          \"s23\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[23], sizeof(t.vreg.s[23]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[23], sizeof(t.vreg.s[23]), 0);\n          },\n          \"s23\")\n      .def_property(\n          \"s24\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[24], sizeof(t.vreg.s[24]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[24], sizeof(t.vreg.s[24]), 0);\n          },\n          \"s24\")\n      .def_property(\n          \"s25\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[25], sizeof(t.vreg.s[25]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[25], sizeof(t.vreg.s[25]), 0);\n          },\n          \"s25\")\n      .def_property(\n          \"s26\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[26], sizeof(t.vreg.s[26]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[26], sizeof(t.vreg.s[26]), 0);\n          },\n          \"s26\")\n      .def_property(\n          \"s27\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[27], sizeof(t.vreg.s[27]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[27], sizeof(t.vreg.s[27]), 0);\n          },\n          \"s27\")\n      .def_property(\n          \"s28\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[28], sizeof(t.vreg.s[28]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[28], sizeof(t.vreg.s[28]), 0);\n          },\n          \"s28\")\n      .def_property(\n          \"s29\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[29], sizeof(t.vreg.s[29]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[29], sizeof(t.vreg.s[29]), 0);\n          },\n          \"s29\")\n      .def_property(\n          \"s30\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[20], sizeof(t.vreg.s[20]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[20], sizeof(t.vreg.s[20]), 0);\n          },\n          \"s30\")\n      .def_property(\n          \"s31\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.s[31], sizeof(t.vreg.s[31]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.s[31], sizeof(t.vreg.s[31]), 0);\n          },\n          \"s31\")\n      .def_property(\n          \"d0\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[0], sizeof(t.vreg.d[0]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[0], sizeof(t.vreg.d[0]), 0);\n          },\n          \"d0\")\n      .def_property(\n          \"d1\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[1], sizeof(t.vreg.d[1]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[1], sizeof(t.vreg.d[1]), 0);\n          },\n          \"d1\")\n      .def_property(\n          \"d2\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[2], sizeof(t.vreg.d[2]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[2], sizeof(t.vreg.d[2]), 0);\n          },\n          \"d2\")\n      .def_property(\n          \"d3\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[3], sizeof(t.vreg.d[3]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[3], sizeof(t.vreg.d[3]), 0);\n          },\n          \"d3\")\n      .def_property(\n          \"d4\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[4], sizeof(t.vreg.d[4]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[4], sizeof(t.vreg.d[4]), 0);\n          },\n          \"d4\")\n      .def_property(\n          \"d5\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[5], sizeof(t.vreg.d[5]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[5], sizeof(t.vreg.d[5]), 0);\n          },\n          \"d5\")\n      .def_property(\n          \"d6\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[6], sizeof(t.vreg.d[6]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[6], sizeof(t.vreg.d[6]), 0);\n          },\n          \"d6\")\n      .def_property(\n          \"d7\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[7], sizeof(t.vreg.d[7]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[7], sizeof(t.vreg.d[7]), 0);\n          },\n          \"d7\")\n      .def_property(\n          \"d8\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[8], sizeof(t.vreg.d[8]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[8], sizeof(t.vreg.d[8]), 0);\n          },\n          \"d8\")\n      .def_property(\n          \"d9\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[9], sizeof(t.vreg.d[9]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[9], sizeof(t.vreg.d[9]), 0);\n          },\n          \"d9\")\n      .def_property(\n          \"d10\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[10], sizeof(t.vreg.d[10]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[10], sizeof(t.vreg.d[10]), 0);\n          },\n          \"d10\")\n      .def_property(\n          \"d11\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[11], sizeof(t.vreg.d[11]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[11], sizeof(t.vreg.d[11]), 0);\n          },\n          \"d11\")\n      .def_property(\n          \"d12\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[12], sizeof(t.vreg.d[12]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[12], sizeof(t.vreg.d[12]), 0);\n          },\n          \"d12\")\n      .def_property(\n          \"d13\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[13], sizeof(t.vreg.d[13]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[13], sizeof(t.vreg.d[13]), 0);\n          },\n          \"d13\")\n      .def_property(\n          \"d14\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[14], sizeof(t.vreg.d[14]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[14], sizeof(t.vreg.d[14]), 0);\n          },\n          \"d14\")\n      .def_property(\n          \"d15\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[15], sizeof(t.vreg.d[15]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[15], sizeof(t.vreg.d[15]), 0);\n          },\n          \"d15\")\n#if QBDI_NUM_FPR == 32\n      .def_property(\n          \"d16\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[16], sizeof(t.vreg.d[16]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[16], sizeof(t.vreg.d[16]), 0);\n          },\n          \"d16\")\n      .def_property(\n          \"d17\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[17], sizeof(t.vreg.d[17]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[17], sizeof(t.vreg.d[17]), 0);\n          },\n          \"d17\")\n      .def_property(\n          \"d18\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[18], sizeof(t.vreg.d[18]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[18], sizeof(t.vreg.d[18]), 0);\n          },\n          \"d18\")\n      .def_property(\n          \"d19\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[19], sizeof(t.vreg.d[19]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[19], sizeof(t.vreg.d[19]), 0);\n          },\n          \"d19\")\n      .def_property(\n          \"d20\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[20], sizeof(t.vreg.d[20]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[20], sizeof(t.vreg.d[20]), 0);\n          },\n          \"d20\")\n      .def_property(\n          \"d21\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[21], sizeof(t.vreg.d[21]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[21], sizeof(t.vreg.d[21]), 0);\n          },\n          \"d21\")\n      .def_property(\n          \"d22\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[22], sizeof(t.vreg.d[22]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[22], sizeof(t.vreg.d[22]), 0);\n          },\n          \"d22\")\n      .def_property(\n          \"d23\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[23], sizeof(t.vreg.d[23]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[23], sizeof(t.vreg.d[23]), 0);\n          },\n          \"d23\")\n      .def_property(\n          \"d24\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[24], sizeof(t.vreg.d[24]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[24], sizeof(t.vreg.d[24]), 0);\n          },\n          \"d24\")\n      .def_property(\n          \"d25\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[25], sizeof(t.vreg.d[25]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[25], sizeof(t.vreg.d[25]), 0);\n          },\n          \"d25\")\n      .def_property(\n          \"d26\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[26], sizeof(t.vreg.d[26]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[26], sizeof(t.vreg.d[26]), 0);\n          },\n          \"d26\")\n      .def_property(\n          \"d27\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[27], sizeof(t.vreg.d[27]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[27], sizeof(t.vreg.d[27]), 0);\n          },\n          \"d27\")\n      .def_property(\n          \"d28\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[28], sizeof(t.vreg.d[28]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[28], sizeof(t.vreg.d[28]), 0);\n          },\n          \"d28\")\n      .def_property(\n          \"d29\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[29], sizeof(t.vreg.d[29]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[29], sizeof(t.vreg.d[29]), 0);\n          },\n          \"d29\")\n      .def_property(\n          \"d30\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[20], sizeof(t.vreg.d[20]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[20], sizeof(t.vreg.d[20]), 0);\n          },\n          \"d30\")\n      .def_property(\n          \"d31\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.d[31], sizeof(t.vreg.d[31]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.d[31], sizeof(t.vreg.d[31]), 0);\n          },\n          \"d31\")\n#endif\n      .def_property(\n          \"q0\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[0], sizeof(t.vreg.q[0]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[0], sizeof(t.vreg.q[0]), 0);\n          },\n          \"q0\")\n      .def_property(\n          \"q1\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[1], sizeof(t.vreg.q[1]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[1], sizeof(t.vreg.q[1]), 0);\n          },\n          \"q1\")\n      .def_property(\n          \"q2\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[2], sizeof(t.vreg.q[2]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[2], sizeof(t.vreg.q[2]), 0);\n          },\n          \"q2\")\n      .def_property(\n          \"q3\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[3], sizeof(t.vreg.q[3]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[3], sizeof(t.vreg.q[3]), 0);\n          },\n          \"q3\")\n      .def_property(\n          \"q4\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[4], sizeof(t.vreg.q[4]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[4], sizeof(t.vreg.q[4]), 0);\n          },\n          \"q4\")\n      .def_property(\n          \"q5\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[5], sizeof(t.vreg.q[5]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[5], sizeof(t.vreg.q[5]), 0);\n          },\n          \"q5\")\n      .def_property(\n          \"q6\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[6], sizeof(t.vreg.q[6]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[6], sizeof(t.vreg.q[6]), 0);\n          },\n          \"q6\")\n      .def_property(\n          \"q7\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[7], sizeof(t.vreg.q[7]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[7], sizeof(t.vreg.q[7]), 0);\n          },\n          \"q7\")\n#if QBDI_NUM_FPR == 32\n      .def_property(\n          \"q8\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[8], sizeof(t.vreg.q[8]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[8], sizeof(t.vreg.q[8]), 0);\n          },\n          \"q8\")\n      .def_property(\n          \"q9\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[9], sizeof(t.vreg.q[9]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[9], sizeof(t.vreg.q[9]), 0);\n          },\n          \"q9\")\n      .def_property(\n          \"q10\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[10], sizeof(t.vreg.q[10]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[10], sizeof(t.vreg.q[10]), 0);\n          },\n          \"q10\")\n      .def_property(\n          \"q11\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[11], sizeof(t.vreg.q[11]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[11], sizeof(t.vreg.q[11]), 0);\n          },\n          \"q11\")\n      .def_property(\n          \"q12\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[12], sizeof(t.vreg.q[12]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[12], sizeof(t.vreg.q[12]), 0);\n          },\n          \"q12\")\n      .def_property(\n          \"q13\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[13], sizeof(t.vreg.q[13]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[13], sizeof(t.vreg.q[13]), 0);\n          },\n          \"q13\")\n      .def_property(\n          \"q14\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[14], sizeof(t.vreg.q[14]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[14], sizeof(t.vreg.q[14]), 0);\n          },\n          \"q14\")\n      .def_property(\n          \"q15\",\n          [](const FPRState &t) {\n            return py::bytes((const char *)&t.vreg.q[15], sizeof(t.vreg.q[15]));\n          },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy((char *)&t.vreg.q[15], sizeof(t.vreg.q[15]), 0);\n          },\n          \"q15\")\n#endif\n      .def(\"__str__\",\n           [](const FPRState &obj) {\n             std::ostringstream oss;\n             oss << std::hex << std::setfill('0')\n                 << \"=== FPRState begin ===\" << std::endl\n                 << \"fpscr  : 0x\" << std::setw(sizeof(rword) * 2) << obj.fpscr\n                 << std::endl\n                 << \"d0  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[0] << std::endl\n                 << \"d1  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[1] << std::endl\n                 << \"d2  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[2] << std::endl\n                 << \"d3  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[3] << std::endl\n                 << \"d4  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[4] << std::endl\n                 << \"d5  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[5] << std::endl\n                 << \"d6  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[6] << std::endl\n                 << \"d7  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[7] << std::endl\n                 << \"d8  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[8] << std::endl\n                 << \"d9  : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[9] << std::endl\n                 << \"d10 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[10] << std::endl\n                 << \"d11 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[11] << std::endl\n                 << \"d12 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[12] << std::endl\n                 << \"d13 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[13] << std::endl\n                 << \"d14 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[14] << std::endl\n                 << \"d15 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[15] << std::endl\n#if QBDI_NUM_FPR == 32\n                 << \"d16 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[16] << std::endl\n                 << \"d17 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[17] << std::endl\n                 << \"d18 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[18] << std::endl\n                 << \"d19 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[19] << std::endl\n                 << \"d20 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[20] << std::endl\n                 << \"d21 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[21] << std::endl\n                 << \"d22 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[22] << std::endl\n                 << \"d23 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[23] << std::endl\n                 << \"d24 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[24] << std::endl\n                 << \"d25 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[25] << std::endl\n                 << \"d26 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[26] << std::endl\n                 << \"d27 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[27] << std::endl\n                 << \"d28 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[28] << std::endl\n                 << \"d29 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[29] << std::endl\n                 << \"d30 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[30] << std::endl\n                 << \"d31 : 0x\" << std::setw(sizeof(uint64_t) * 2)\n                 << (uint64_t)obj.vreg.d[31] << std::endl\n#endif\n                 << \"=== FPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\"__copy__\", [](const FPRState &state) -> FPRState { return state; })\n      .def(py::pickle(\n          [](const FPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"ARM\", py::bytes(reinterpret_cast<const char *>(&state),\n                                 sizeof(FPRState)));\n          },\n          [](py::tuple t) -> FPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"ARM\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"ARM\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(FPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(FPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            FPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(FPRState));\n            return newState;\n\n          }));\n\n  m.attr(\"REG_RETURN\") = REG_RETURN;\n  m.attr(\"AVAILABLE_GPR\") = AVAILABLE_GPR;\n  m.attr(\"REG_BP\") = REG_BP;\n  m.attr(\"REG_SP\") = REG_SP;\n  m.attr(\"REG_PC\") = REG_PC;\n  m.attr(\"NUM_GPR\") = NUM_GPR;\n  m.attr(\"REG_LR\") = REG_LR;\n  m.attr(\"REG_FLAG\") = REG_FLAG;\n\n  py::class_<GPRState>(m, \"GPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"r0\", &GPRState::r0)\n      .def_readwrite(\"r1\", &GPRState::r1)\n      .def_readwrite(\"r2\", &GPRState::r2)\n      .def_readwrite(\"r3\", &GPRState::r3)\n      .def_readwrite(\"r4\", &GPRState::r4)\n      .def_readwrite(\"r5\", &GPRState::r5)\n      .def_readwrite(\"r6\", &GPRState::r6)\n      .def_readwrite(\"r7\", &GPRState::r7)\n      .def_readwrite(\"r8\", &GPRState::r8)\n      .def_readwrite(\"r9\", &GPRState::r9)\n      .def_readwrite(\"r10\", &GPRState::r10)\n      .def_readwrite(\"r11\", &GPRState::r11)\n      .def_readwrite(\"r12\", &GPRState::r12)\n      .def_readwrite(\"fp\", &GPRState::r12, \"shadow of r12\")\n      .def_readwrite(\"sp\", &GPRState::sp)\n      .def_readwrite(\"lr\", &GPRState::lr)\n      .def_readwrite(\"pc\", &GPRState::pc)\n      .def_readwrite(\"cpsr\", &GPRState::cpsr)\n      .def_property(\n          \"localMonitor_addr\",\n          [](const GPRState &t) { return py::int_(t.localMonitor.addr); },\n          [](GPRState &t, py::int_ v) { t.localMonitor.addr = v; },\n          \"localMonitor : exclusive base address\")\n      .def_property(\n          \"localMonitor_enable\",\n          [](const GPRState &t) { return py::int_(t.localMonitor.enable); },\n          [](GPRState &t, py::int_ v) { t.localMonitor.enable = v; },\n          \"localMonitor : exclusive state\")\n      // cross architecture access\n      .def_readwrite(\"REG_RETURN\", &GPRState::r0, \"shadow of r0\")\n      .def_readwrite(\"AVAILABLE_GPR\", &GPRState::sp, \"shadow of sp\")\n      .def_readwrite(\"REG_BP\", &GPRState::r12, \"shadow of r12 / r12\")\n      .def_readwrite(\"REG_SP\", &GPRState::sp, \"shadow of sp\")\n      .def_readwrite(\"REG_PC\", &GPRState::pc, \"shadow of PC\")\n      .def_readwrite(\"NUM_GPR\", &GPRState::cpsr, \"shadow of cpsr\")\n      .def_readwrite(\"REG_LR\", &GPRState::lr, \"shadow of lr\")\n      .def_readwrite(\"REG_FLAG\", &GPRState::cpsr, \"shadow of cpsr\")\n      .def(\"__str__\",\n           [](const GPRState &obj) {\n             std::ostringstream oss;\n             int r = sizeof(rword) * 2;\n             oss << std::hex << std::setfill('0')\n                 << \"=== GPRState begin ===\" << std::endl\n                 << \"r0     : 0x\" << std::setw(r) << obj.r0 << std::endl\n                 << \"r1     : 0x\" << std::setw(r) << obj.r1 << std::endl\n                 << \"r2     : 0x\" << std::setw(r) << obj.r2 << std::endl\n                 << \"r3     : 0x\" << std::setw(r) << obj.r3 << std::endl\n                 << \"r4     : 0x\" << std::setw(r) << obj.r4 << std::endl\n                 << \"r5     : 0x\" << std::setw(r) << obj.r5 << std::endl\n                 << \"r6     : 0x\" << std::setw(r) << obj.r6 << std::endl\n                 << \"r7     : 0x\" << std::setw(r) << obj.r7 << std::endl\n                 << \"r8     : 0x\" << std::setw(r) << obj.r8 << std::endl\n                 << \"r9     : 0x\" << std::setw(r) << obj.r9 << std::endl\n                 << \"r10    : 0x\" << std::setw(r) << obj.r10 << std::endl\n                 << \"r11    : 0x\" << std::setw(r) << obj.r11 << std::endl\n                 << \"r12|FP : 0x\" << std::setw(r) << obj.r12 << std::endl\n                 << \"SP     : 0x\" << std::setw(r) << obj.sp << std::endl\n                 << \"LR     : 0x\" << std::setw(r) << obj.lr << std::endl\n                 << \"PC     : 0x\" << std::setw(r) << obj.pc << std::endl\n                 << \"cpsr   : 0x\" << std::setw(r) << obj.cpsr << std::endl\n                 << \"=== GPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\n          \"__getitem__\",\n          [](const GPRState &obj, unsigned int index) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_GET(&obj, index);\n          },\n          \"Get a register like QBDI_GPR_GET\", \"index\"_a)\n      .def(\n          \"__setitem__\",\n          [](GPRState &obj, unsigned int index, rword value) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_SET(&obj, index, value);\n          },\n          \"Set a register like QBDI_GPR_SET\", \"index\"_a, \"value\"_a)\n      .def(\"__copy__\", [](const GPRState &state) -> GPRState { return state; })\n      .def(py::pickle(\n          [](const GPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"ARM\", py::bytes(reinterpret_cast<const char *>(&state),\n                                 sizeof(GPRState)));\n          },\n          [](py::tuple t) -> GPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"ARM\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"ARM\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(GPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(GPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            GPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(GPRState));\n            return newState;\n\n          }));\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/CMakeLists.txt",
    "content": "target_sources(\n  pyqbdi_module\n  INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/Callback.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/Errors.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/InstAnalysis.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/Logs.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/Memory.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/Range.cpp\"\n            \"${CMAKE_CURRENT_LIST_DIR}/VM.cpp\")\n\ntarget_include_directories(pyqbdi_module INTERFACE ${CMAKE_CURRENT_LIST_DIR})\n\nif(QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n"
  },
  {
    "path": "tools/pyqbdi/binding/Callback.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Enum.hpp\"\n#include \"callback_python.h\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Callback(py::module_ &m) {\n\n  py::enum_<VMAction>(m, \"VMAction\", \"The callback results.\")\n      .value(\"CONTINUE\", VMAction::CONTINUE,\n             \"The execution of the basic block continues.\")\n      .value(\"SKIP_INST\", VMAction::SKIP_INST,\n             \"Available only with PREINST InstCallback. The instruction and \"\n             \"the remained PREINST callbacks are skip. The execution continue \"\n             \"with the POSTINST instruction.\\n\\nWe recommand to used this \"\n             \"result with a low priority PREINST callback in order to emulate \"\n             \"the instruction without skipping the POSTINST callback.\")\n      .value(\"SKIP_PATCH\", VMAction::SKIP_PATCH,\n             \"Available only with InstCallback. The current instruction and \"\n             \"the reminding callback (PRE and POST) are skip. The execution \"\n             \"continues to the next instruction.\\n\\nFor instruction that \"\n             \"change the instruction pointer (jump/call/ret), BREAK_TO_VM must \"\n             \"be used insted of SKIP.\\n\\nSKIP can break the record of \"\n             \"MemoryAccess for the current instruction.\")\n      .value(\"BREAK_TO_VM\", VMAction::BREAK_TO_VM,\n             \"The execution breaks and returns to the VM causing a complete \"\n             \"reevaluation of \"\n             \"the execution state. A BREAK_TO_VM is needed to ensure that \"\n             \"modifications of \"\n             \"the Program Counter or the program code are taken into account.\")\n      .value(\"STOP\", VMAction::STOP,\n             \"Stops the execution of the program. This causes the run function \"\n             \"to return early.\")\n      .export_values();\n\n  py::enum_<InstPosition>(m, \"InstPosition\",\n                          \"Position relative to an instruction.\")\n      .value(\"PREINST\", InstPosition::PREINST,\n             \"Positioned before the instruction.\")\n      .value(\"POSTINST\", InstPosition::POSTINST,\n             \"Positioned after the instruction.\")\n      .export_values();\n\n  py::enum_<CallbackPriority>(m, \"CallbackPriority\", \"Priority of callback.\")\n      .value(\"PRIORITY_DEFAULT\", CallbackPriority::PRIORITY_DEFAULT,\n             \"Default priority for callback.\")\n      .value(\"PRIORITY_MEMACCESS_LIMIT\",\n             CallbackPriority::PRIORITY_MEMACCESS_LIMIT,\n             \"Maximum priority if getInstMemoryAccess \"\n             \"is used in the callback.\")\n      .export_values();\n\n  enum_int_flag_<VMEvent>(m, \"VMEvent\", py::arithmetic())\n      .value(\"SEQUENCE_ENTRY\", VMEvent::SEQUENCE_ENTRY,\n             \"Triggered when the execution enters a sequence.\")\n      .value(\"SEQUENCE_EXIT\", VMEvent::SEQUENCE_EXIT,\n             \"Triggered when the execution exits from the current sequence.\")\n      .value(\"BASIC_BLOCK_ENTRY\", VMEvent::BASIC_BLOCK_ENTRY,\n             \"Triggered when the execution enters a basic block.\")\n      .value(\"BASIC_BLOCK_EXIT\", VMEvent::BASIC_BLOCK_EXIT,\n             \"Triggered when the execution exits from the current basic block.\")\n      .value(\n          \"BASIC_BLOCK_NEW\", VMEvent::BASIC_BLOCK_NEW,\n          \"Triggered when the execution enters a new (~unknown) basic block.\")\n      .value(\"EXEC_TRANSFER_CALL\", VMEvent::EXEC_TRANSFER_CALL,\n             \"Triggered when the ExecBroker executes an execution transfer.\")\n      .value(\n          \"EXEC_TRANSFER_RETURN\", VMEvent::EXEC_TRANSFER_RETURN,\n          \"Triggered when the ExecBroker returns from an execution transfer.\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n\n  enum_int_flag_<MemoryAccessType>(m, \"MemoryAccessType\",\n                                   \"Memory access type (read / write / ...)\",\n                                   py::arithmetic())\n      .value(\"MEMORY_READ\", MemoryAccessType::MEMORY_READ, \"Memory read access\")\n      .value(\"MEMORY_WRITE\", MemoryAccessType::MEMORY_WRITE,\n             \"Memory write access\")\n      .value(\"MEMORY_READ_WRITE\", MemoryAccessType::MEMORY_READ_WRITE,\n             \"Memory read/write access\")\n      .export_values()\n      .def_invert();\n\n  py::class_<VMState>(m, \"VMState\")\n      .def_readonly(\"event\", &VMState::event,\n                    \"The event(s) which triggered the callback (must be \"\n                    \"checked using a mask: event & BASIC_BLOCK_ENTRY).\")\n      .def_readonly(\"basicBlockStart\", &VMState::basicBlockStart,\n                    \"The current basic block start address which can also be \"\n                    \"the execution transfer destination.\")\n      .def_readonly(\"basicBlockEnd\", &VMState::basicBlockEnd,\n                    \"The current basic block end address which can also be the \"\n                    \"execution transfer destination.\")\n      .def_readonly(\"sequenceStart\", &VMState::sequenceStart,\n                    \"The current sequence start address which can also be the \"\n                    \"execution transfer destination.\")\n      .def_readonly(\"sequenceEnd\", &VMState::sequenceEnd,\n                    \"The current sequence end address which can also be the \"\n                    \"execution transfer destination.\")\n      .def(\"__repr__\",\n           [](const VMState &state) {\n             std::ostringstream oss;\n\n             oss << std::hex << std::setfill('0') << \"<VMState \"\n                 << py::str(py::cast(state.event)) << \", \"\n                 << state.basicBlockStart << \", \" << state.basicBlockEnd << \", \"\n                 << state.sequenceStart << \", \" << state.sequenceEnd << \">\";\n             return oss.str();\n           })\n      .def(\"__copy__\", [](const VMState &state) -> VMState { return state; })\n      .def(py::pickle(\n          [](const VMState &state) { // __getstate__\n            return py::make_tuple(state.event, state.basicBlockStart,\n                                  state.basicBlockEnd, state.sequenceStart,\n                                  state.sequenceEnd);\n          },\n          [](py::tuple t) -> VMState { // __setstate__\n            if (t.size() != 5)\n              throw std::runtime_error(\"Invalid state!\");\n\n            return {t[0].cast<VMEvent>(), t[1].cast<rword>(),\n                    t[2].cast<rword>(), t[3].cast<rword>(), t[4].cast<rword>()};\n          }));\n\n  enum_int_flag_<MemoryAccessFlags>(m, \"MemoryAccessFlags\",\n                                    \"Memory access flags\", py::arithmetic())\n      .value(\"MEMORY_NO_FLAGS\", MemoryAccessFlags::MEMORY_NO_FLAGS,\n             \"Empty flags\")\n      .value(\"MEMORY_UNKNOWN_SIZE\", MemoryAccessFlags::MEMORY_UNKNOWN_SIZE,\n             \"The size of the access isn't known.\")\n      .value(\"MEMORY_MINIMUM_SIZE\", MemoryAccessFlags::MEMORY_MINIMUM_SIZE,\n             \"The given size is a minimum size.\")\n      .value(\"MEMORY_UNKNOWN_VALUE\", MemoryAccessFlags::MEMORY_UNKNOWN_VALUE,\n             \"The value of the access is unknown or hasn't been retrived.\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n\n  py::class_<MemoryAccess>(m, \"MemoryAccess\")\n      .def_readwrite(\"instAddress\", &MemoryAccess::instAddress,\n                     \"Address of instruction making the access\")\n      .def_readwrite(\"accessAddress\", &MemoryAccess::accessAddress,\n                     \"Address of accessed memory\")\n      .def_readwrite(\"value\", &MemoryAccess::value,\n                     \"Value read from / written to memory\")\n      .def_readwrite(\"size\", &MemoryAccess::size,\n                     \"Size of memory access (in bytes)\")\n      .def_readwrite(\"type\", &MemoryAccess::type,\n                     \"Memory access type (READ / WRITE)\")\n      .def_readwrite(\"flags\", &MemoryAccess::flags, \"Memory access flags\")\n      .def(\"__repr__\",\n           [](const MemoryAccess &acc) {\n             std::ostringstream oss;\n\n             oss << std::hex << std::setfill('0') << \"<MemoryAccess \"\n                 << acc.instAddress << \", \" << acc.accessAddress << \", \"\n                 << acc.value << \", \" << acc.size << \", \"\n                 << py::str(py::cast(acc.type)) << \", \"\n                 << py::str(py::cast(acc.flags)) << \">\";\n             return oss.str();\n           })\n      .def(\"__copy__\",\n           [](const MemoryAccess &acc) -> MemoryAccess { return acc; })\n      .def(py::pickle(\n          [](const MemoryAccess &acc) { // __getstate__\n            return py::make_tuple(acc.instAddress, acc.accessAddress, acc.value,\n                                  acc.size, acc.type, acc.flags);\n          },\n          [](py::tuple t) -> MemoryAccess { // __setstate__\n            if (t.size() != 6)\n              throw std::runtime_error(\"Invalid state!\");\n\n            return {t[0].cast<rword>(),\n                    t[1].cast<rword>(),\n                    t[2].cast<rword>(),\n                    t[3].cast<uint16_t>(),\n                    t[4].cast<MemoryAccessType>(),\n                    t[5].cast<MemoryAccessFlags>()};\n          }));\n\n  py::class_<InstrRuleDataCBKPython>(m, \"InstrRuleDataCBK\")\n      .def(py::init<PyInstCallback &, py::object &, InstPosition, int>(),\n           \"cbk\"_a, \"data\"_a, \"position\"_a, \"priority\"_a = PRIORITY_DEFAULT)\n      .def_readwrite(\n          \"cbk\", &InstrRuleDataCBKPython::cbk,\n          \"Address of the function to call when the instruction is executed\")\n      .def_readwrite(\"data\", &InstrRuleDataCBKPython::data,\n                     \"User defined data which will be forward to cbk.\")\n      .def_readwrite(\n          \"position\", &InstrRuleDataCBKPython::position,\n          \"Relative position of the event callback (PREINST / POSTINST).\")\n      .def_readwrite(\"priority\", &InstrRuleDataCBKPython::priority,\n                     \"Priority of the callback.\");\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/Enum.hpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef PYQBDI_ENUM_HPP_\n#define PYQBDI_ENUM_HPP_\n\n#include <map>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\nnamespace py = pybind11;\n\nnamespace QBDI {\nnamespace pyQBDI {\n\ntemplate <class Type>\nclass enum_int_flag_ : public pybind11::enum_<Type> {\npublic:\n  using py::enum_<Type>::def;\n  using py::enum_<Type>::def_property_readonly_static;\n  using Scalar = typename py::enum_<Type>::Scalar;\n\nprivate:\n  std::string enum_name;\n  std::map<Scalar, std::string> members_names;\n  Scalar mask;\n  bool is_arithmetic;\n\npublic:\n  template <typename... Extra>\n  enum_int_flag_(const py::handle &scope, const char *name,\n                 const Extra &...extra)\n      : py::enum_<Type>{scope, name, extra...}, enum_name(name), mask(0) {\n    constexpr bool is_arithmetic_ =\n        py::detail::any_of<std::is_same<py::arithmetic, Extra>...>::value;\n    is_arithmetic = is_arithmetic_;\n    def(\"__eq__\",\n        [](const Type &value, const Type &value2) { return value == value2; });\n    def(\"__ne__\",\n        [](const Type &value, const Type &value2) { return value != value2; });\n    if (is_arithmetic_) {\n      def(\n          \"__and__\",\n          [](const Type &value, const Type &value2) {\n            return static_cast<Type>(value & value2);\n          },\n          py::prepend());\n      def(\n          \"__or__\",\n          [](const Type &value, const Type &value2) {\n            return static_cast<Type>(value | value2);\n          },\n          py::prepend());\n      def(\n          \"__xor__\",\n          [](const Type &value, const Type &value2) {\n            return static_cast<Type>(value ^ value2);\n          },\n          py::prepend());\n    }\n  }\n\n  enum_int_flag_ &value(char const *name, Type value,\n                        const char *doc = nullptr) {\n    members_names[(Scalar)value] = std::string(name);\n    mask |= (Scalar)value;\n    pybind11::enum_<Type>::value(name, value, doc);\n    return *this;\n  }\n\n  enum_int_flag_ &export_values() {\n    pybind11::enum_<Type>::export_values();\n    return *this;\n  }\n\n  enum_int_flag_ &def_invert() {\n    if (is_arithmetic) {\n      def(\n          \"__invert__\",\n          [m = mask](const Type &value) {\n            return static_cast<Type>(value ^ m);\n          },\n          py::prepend());\n    }\n    return *this;\n  }\n\n  enum_int_flag_ &def_repr_str() {\n    def(\n        \"__str__\",\n        [enum_name = enum_name,\n         members_names = members_names](const Type &value) {\n          return py::str(\"{}.{}\").format(\n              enum_name,\n              enum_int_flag_<Type>::get_member_name(members_names, value));\n        },\n        py::prepend());\n    def(\n        \"__repr__\",\n        [enum_name = enum_name,\n         members_names = members_names](const Type &value) {\n          return py::str(\"<{}.{}: {}>\")\n              .format(\n                  enum_name,\n                  enum_int_flag_<Type>::get_member_name(members_names, value),\n                  (Scalar)value);\n        },\n        py::prepend());\n\n    return *this;\n  }\n\n  static inline std::string\n  get_member_name(const std::map<Scalar, std::string> &members_names,\n                  Scalar v) {\n    const auto it = members_names.find(v);\n    if (it != members_names.end()) {\n      return it->second;\n    }\n    if (v == 0) {\n      return \"0\";\n    }\n    std::string res;\n    for (unsigned int i = 0; i < sizeof(Scalar) * 8 && v != 0; i++) {\n      if ((v & (1 << i)) != 0) {\n        const auto it = members_names.find(1 << i);\n        if (it != members_names.end()) {\n          if (res.size() != 0) {\n            res += \"|\";\n          }\n          res += it->second;\n        }\n        v ^= (1 << i);\n      }\n    }\n    if (res.size() == 0) {\n      return \"???\";\n    }\n    return res;\n  }\n};\n} // namespace pyQBDI\n} // namespace QBDI\n\n#endif\n"
  },
  {
    "path": "tools/pyqbdi/binding/Errors.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Errors(py::module_ &m) {\n\n  py::enum_<VMError>(m, \"VMError\", \"QBDI Error values\")\n      .value(\"INVALID_EVENTID\", VMError::INVALID_EVENTID,\n             \"Mark a returned event id as invalid\")\n      .export_values();\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/InstAnalysis.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Enum.hpp\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\ntemplate <class T>\npy::object get_InstAnalysis_member(const InstAnalysis &obj,\n                                   T InstAnalysis::*member, uint32_t v) {\n  if (obj.analysisType & v) {\n    return py::cast(obj.*member);\n  } else {\n    return py::none();\n  }\n}\n\n// specialization for symbol and module if not found\ntemplate <>\npy::object get_InstAnalysis_member(const InstAnalysis &obj,\n                                   char *InstAnalysis::*member, uint32_t v) {\n  if (obj.analysisType & v && obj.*member != NULL) {\n    return py::cast(obj.*member);\n  } else {\n    return py::none();\n  }\n}\n\nvoid init_binding_InstAnalysis(py::module_ &m) {\n\n  enum_int_flag_<RegisterAccessType>(\n      m, \"RegisterAccessType\", \"Access type (R/W/RW) of a register operand\",\n      py::arithmetic())\n      .value(\"REGISTER_UNUSED\", RegisterAccessType::REGISTER_UNUSED,\n             \"Unused register\")\n      .value(\"REGISTER_READ\", RegisterAccessType::REGISTER_READ,\n             \"Register read access\")\n      .value(\"REGISTER_WRITE\", RegisterAccessType::REGISTER_WRITE,\n             \"Register write access\")\n      .value(\"REGISTER_READ_WRITE\", RegisterAccessType::REGISTER_READ_WRITE,\n             \"Register read/write access\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n\n  py::enum_<ConditionType>(m, \"ConditionType\", \"Condition type\")\n      .value(\"CONDITION_NONE\", ConditionType::CONDITION_NONE,\n             \"The instruction is unconditionnal\")\n      .value(\"CONDITION_ALWAYS\", ConditionType::CONDITION_ALWAYS,\n             \"The instruction is always true\")\n      .value(\"CONDITION_NEVER\", ConditionType::CONDITION_NEVER,\n             \"The instruction is always false\")\n      .value(\"CONDITION_EQUALS\", ConditionType::CONDITION_EQUALS,\n             \"Equals ( '==' )\")\n      .value(\"CONDITION_NOT_EQUALS\", ConditionType::CONDITION_NOT_EQUALS,\n             \"Not Equals ( '!=' )\")\n      .value(\"CONDITION_ABOVE\", ConditionType::CONDITION_ABOVE,\n             \"Above ( '>' unsigned )\")\n      .value(\"CONDITION_BELOW_EQUALS\", ConditionType::CONDITION_BELOW_EQUALS,\n             \"Below or Equals ( '<=' unsigned )\")\n      .value(\"CONDITION_ABOVE_EQUALS\", ConditionType::CONDITION_ABOVE_EQUALS,\n             \"Above or Equals ( '>=' unsigned )\")\n      .value(\"CONDITION_BELOW\", ConditionType::CONDITION_BELOW,\n             \"Below ( '<' unsigned )\")\n      .value(\"CONDITION_GREAT\", ConditionType::CONDITION_GREAT,\n             \"Great ( '>' signed )\")\n      .value(\"CONDITION_LESS_EQUALS\", ConditionType::CONDITION_LESS_EQUALS,\n             \"Less or Equals ( '<=' signed )\")\n      .value(\"CONDITION_GREAT_EQUALS\", ConditionType::CONDITION_GREAT_EQUALS,\n             \"Great or Equals ( '>=' signed )\")\n      .value(\"CONDITION_LESS\", ConditionType::CONDITION_LESS,\n             \"Less ( '<' signed )\")\n      .value(\"CONDITION_EVEN\", ConditionType::CONDITION_EVEN, \"Even\")\n      .value(\"CONDITION_ODD\", ConditionType::CONDITION_ODD, \"Odd\")\n      .value(\"CONDITION_OVERFLOW\", ConditionType::CONDITION_OVERFLOW,\n             \"Overflow\")\n      .value(\"CONDITION_NOT_OVERFLOW\", ConditionType::CONDITION_NOT_OVERFLOW,\n             \"Not Overflow\")\n      .value(\"CONDITION_SIGN\", ConditionType::CONDITION_SIGN, \"Sign\")\n      .value(\"CONDITION_NOT_SIGN\", ConditionType::CONDITION_NOT_SIGN,\n             \"Not Sign\")\n      .export_values();\n\n  py::enum_<OperandType>(m, \"OperandType\", \"Operand type\")\n      .value(\"OPERAND_INVALID\", OperandType::OPERAND_INVALID, \"Invalid operand\")\n      .value(\"OPERAND_IMM\", OperandType::OPERAND_IMM, \"Immediate operand\")\n      .value(\"OPERAND_GPR\", OperandType::OPERAND_GPR, \"Register operand\")\n      .value(\"OPERAND_PRED\", OperandType::OPERAND_PRED, \"Predicate operand\")\n      .value(\"OPERAND_FPR\", OperandType::OPERAND_FPR, \"Float register operand \")\n      .value(\"OPERAND_SEG\", OperandType::OPERAND_SEG,\n             \"Segment or unsupported register operand\")\n      .export_values();\n\n  enum_int_flag_<OperandFlag>(m, \"OperandFlag\", \"Operand flag\",\n                              py::arithmetic())\n      .value(\"OPERANDFLAG_NONE\", OperandFlag::OPERANDFLAG_NONE, \"No flag\")\n      .value(\"OPERANDFLAG_ADDR\", OperandFlag::OPERANDFLAG_ADDR,\n             \"The operand is used to compute an address\")\n      .value(\"OPERANDFLAG_PCREL\", OperandFlag::OPERANDFLAG_PCREL,\n             \"The value of the operand is PC relative\")\n      .value(\"OPERANDFLAG_UNDEFINED_EFFECT\",\n             OperandFlag::OPERANDFLAG_UNDEFINED_EFFECT,\n             \"The operand role isn’t fully defined\")\n      .value(\"OPERANDFLAG_IMPLICIT\", OperandFlag::OPERANDFLAG_IMPLICIT,\n             \"The operand is implicit\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n\n  enum_int_flag_<AnalysisType>(m, \"AnalysisType\", \"Instruction analysis type\",\n                               py::arithmetic())\n      .value(\"ANALYSIS_INSTRUCTION\", AnalysisType::ANALYSIS_INSTRUCTION,\n             \"Instruction analysis (address, mnemonic, ...)\")\n      .value(\"ANALYSIS_DISASSEMBLY\", AnalysisType::ANALYSIS_DISASSEMBLY,\n             \"Instruction disassembly\")\n      .value(\"ANALYSIS_OPERANDS\", AnalysisType::ANALYSIS_OPERANDS,\n             \"Instruction operands analysis\")\n      .value(\"ANALYSIS_SYMBOL\", AnalysisType::ANALYSIS_SYMBOL,\n             \"Instruction symbol\")\n      .value(\"ANALYSIS_JIT\", AnalysisType::ANALYSIS_JIT, \"QBDI JIT Information\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n\n  py::class_<OperandAnalysis>(m, \"OperandAnalysis\")\n      .def_readonly(\"type\", &OperandAnalysis::type, \"Operand type\")\n      .def_readonly(\"flag\", &OperandAnalysis::flag, \"Operand flag\")\n      .def_readonly(\"value\", &OperandAnalysis::value,\n                    \"Operand value (if immediate), or register Id\")\n      .def_readonly(\"size\", &OperandAnalysis::size, \"Operand size (in bytes)\")\n      .def_readonly(\"regOff\", &OperandAnalysis::regOff,\n                    \"Sub-register offset in register (in bits)\")\n      .def_readonly(\"regCtxIdx\", &OperandAnalysis::regCtxIdx,\n                    \"Register index in VM state\")\n      .def_readonly(\"regName\", &OperandAnalysis::regName, \"Register name\")\n      .def_readonly(\"regAccess\", &OperandAnalysis::regAccess,\n                    \"Register access type (r, w, rw)\");\n\n  py::class_<InstAnalysis>(m, \"InstAnalysis\")\n      // ANALYSIS_INSTRUCTION\n      .def_property_readonly(\n          \"mnemonic\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::mnemonic,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"LLVM mnemonic (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"address\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::address,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"Instruction address (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"instSize\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::instSize,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"Instruction size (in bytes) (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"affectControlFlow\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(\n                obj, &InstAnalysis::affectControlFlow, ANALYSIS_INSTRUCTION);\n          },\n          \"True if instruction affects control flow (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"isBranch\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::isBranch,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if instruction acts like a 'jump' (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"isCall\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::isCall,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if instruction acts like a 'call' (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"isReturn\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::isReturn,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if instruction acts like a 'return' (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"isCompare\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::isCompare,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if instruction is a comparison (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"isPredicable\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::isPredicable,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if instruction contains a predicate (~is conditional) (if \"\n          \"ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"isMoveImm\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::isMoveImm,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if this instruction is a move immediate (including conditional\"\n          \" moves) instruction. (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"mayLoad\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::mayLoad,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if QBDI detects a load for this instruction (if \"\n          \"ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"mayStore\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::mayStore,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"True if QBDI detects a store for this instruction (if \"\n          \"ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"loadSize\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::loadSize,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"size of the expected read access (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"storeSize\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::storeSize,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"size of the expected write access (if ANALYSIS_INSTRUCTION)\")\n      .def_property_readonly(\n          \"condition\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::condition,\n                                           ANALYSIS_INSTRUCTION);\n          },\n          \"Condition associated with the instruction (if ANALYSIS_INSTRUCTION)\")\n      // ANALYSIS_DISASSEMBLY\n      .def_property_readonly(\n          \"disassembly\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::disassembly,\n                                           ANALYSIS_DISASSEMBLY);\n          },\n          \"Instruction disassembly (if ANALYSIS_DISASSEMBLY)\")\n      // ANALYSIS_OPERANDS\n      .def_property_readonly(\n          \"flagsAccess\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::flagsAccess,\n                                           ANALYSIS_OPERANDS);\n          },\n          \"Flag access type (noaccess, r, w, rw) (if ANALYSIS_OPERANDS)\")\n      .def_property_readonly(\n          \"numOperands\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::numOperands,\n                                           ANALYSIS_OPERANDS);\n          },\n          \"Number of operands used by the instruction (if ANALYSIS_OPERANDS)\")\n      .def_property_readonly(\n          \"operands\",\n          [](const InstAnalysis &obj) {\n            if (obj.analysisType & ANALYSIS_OPERANDS) {\n              std::vector<OperandAnalysis *> operandslist;\n              for (int i = 0; i < obj.numOperands; i++) {\n                operandslist.push_back(&(obj.operands[i]));\n              }\n              return static_cast<py::object>(py::tuple(py::cast(operandslist)));\n            } else {\n              return static_cast<py::object>(py::none());\n            }\n          },\n          \"Structure containing analysis results of an operand provided by the \"\n          \"VM (if ANALYSIS_OPERANDS)\")\n      // ANALYSIS_SYMBOL\n      .def_property_readonly(\n          \"symbolName\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::symbolName,\n                                           ANALYSIS_SYMBOL);\n          },\n          \"Instruction symbol (if ANALYSIS_SYMBOL and found)\")\n      .def_property_readonly(\n          \"symbolOffset\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::symbolOffset,\n                                           ANALYSIS_SYMBOL);\n          },\n          \"Instruction symbol offset (if ANALYSIS_SYMBOL)\")\n      .def_property_readonly(\n          \"moduleName\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::moduleName,\n                                           ANALYSIS_SYMBOL);\n          },\n          \"Instruction module name (if ANALYSIS_SYMBOL and found)\")\n      // deprecated Name\n      .def_property_readonly(\n          \"symbol\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::symbolName,\n                                           ANALYSIS_SYMBOL);\n          },\n          \"Instruction symbol (if ANALYSIS_SYMBOL and found) (deprecated)\")\n      .def_property_readonly(\n          \"module\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::moduleName,\n                                           ANALYSIS_SYMBOL);\n          },\n          \"Instruction module name (if ANALYSIS_SYMBOL and found) \"\n          \"(deprecated)\")\n      // ANALYSIS_JIT\n      .def_property_readonly(\n          \"patchAddress\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::patchAddress,\n                                           ANALYSIS_JIT);\n          },\n          \"Begin of the instrumentation patch for this instruction (if \"\n          \"ANALYSIS_JIT)\")\n      .def_property_readonly(\n          \"patchSize\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::patchSize,\n                                           ANALYSIS_JIT);\n          },\n          \"Whole size of the instrumentation patch (if \"\n          \"ANALYSIS_JIT)\")\n      .def_property_readonly(\n          \"patchInstOffset\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::patchInstOffset,\n                                           ANALYSIS_JIT);\n          },\n          \"Offset of the JIT instruction in the path (if \"\n          \"ANALYSIS_JIT)\")\n      .def_property_readonly(\n          \"patchInstSize\",\n          [](const InstAnalysis &obj) {\n            return get_InstAnalysis_member(obj, &InstAnalysis::patchInstSize,\n                                           ANALYSIS_JIT);\n          },\n          \"Size of the JIT instruction in the path (if \"\n          \"ANALYSIS_JIT)\");\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/Logs.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Logs(py::module_ &m) {\n\n  py::enum_<LogPriority>(\n      m, \"LogPriority\",\n      \"Each log has a priority (or level) which can be used to control \"\n      \"verbosity\\n\"\n      \"In production builds, only Warning and Error logs are kept.\")\n      .value(\"DEBUG\", LogPriority::DEBUG, \"Debug logs\")\n      .value(\"INFO\", LogPriority::INFO, \"Info logs (default)\")\n      .value(\"WARNING\", LogPriority::WARNING, \"Warning logs\")\n      .value(\"ERROR\", LogPriority::ERROR, \"Error logs\")\n      .value(\"DISABLE\", LogPriority::DISABLE, \"Disable logs message\")\n      .export_values();\n\n  m.def(\"setLogPriority\", &setLogPriority, \"Enable logs matching priority.\",\n        \"priority\"_a);\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/Memory.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Enum.hpp\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Memory(py::module_ &m) {\n\n  enum_int_flag_<Permission>(m, \"Permission\", \"Memory access rights.\",\n                             py::arithmetic())\n      .value(\"PF_NONE\", Permission::PF_NONE, \"No access\")\n      .value(\"PF_READ\", Permission::PF_READ, \"Read access\")\n      .value(\"PF_WRITE\", Permission::PF_WRITE, \"Write access\")\n      .value(\"PF_EXEC\", Permission::PF_EXEC, \"Execution access\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n\n  py::class_<MemoryMap>(m, \"MemoryMap\")\n      .def_readwrite(\"range\", &MemoryMap::range,\n                     \"A range of memory (region), delimited between a start \"\n                     \"and an (excluded) end address.\")\n      .def_property(\n          \"permission\", [](const MemoryMap &map) { return map.permission; },\n          [](MemoryMap &map, int permission) {\n            map.permission = static_cast<Permission>(permission);\n          },\n          \"Region access rights (PF_READ, PF_WRITE, PF_EXEC).\")\n      .def_readwrite(\"name\", &MemoryMap::name,\n                     \"Region name (useful when a region is mapping a module).\")\n      .def(\"__repr__\",\n           [](const MemoryMap &map) {\n             std::ostringstream oss;\n\n             oss << std::hex << std::setfill('0') << \"<MemoryMap [\"\n                 << map.range.start() << \", \" << map.range.end() << \"), \"\n                 << py::str(py::cast(map.permission)) << \", \\\"\" << map.name\n                 << \"\\\">\";\n             return oss.str();\n           })\n      .def(\"__copy__\", [](const MemoryMap &map) -> MemoryMap { return map; })\n      .def(py::pickle(\n          [](const MemoryMap &map) { // __getstate__\n            return py::make_tuple(map.range.start(), map.range.end(),\n                                  map.permission, map.name);\n          },\n          [](py::tuple t) -> MemoryMap { // __setstate__\n            if (t.size() != 4)\n              throw std::runtime_error(\"Invalid state!\");\n\n            return {t[0].cast<rword>(), t[1].cast<rword>(),\n                    t[2].cast<Permission>(), t[3].cast<std::string>()};\n          }));\n\n  m.def(\"getRemoteProcessMaps\", &getRemoteProcessMaps,\n        \"Get a list of all the memory maps (regions) of a process.\", \"pid\"_a,\n        \"full_path\"_a = false);\n\n  m.def(\"getCurrentProcessMaps\", &getCurrentProcessMaps,\n        \"Get a list of all the memory maps (regions) of the current process.\",\n        \"full_path\"_a = false);\n\n  m.def(\"getModuleNames\",\n        static_cast<std::vector<std::string> (*)()>(&getModuleNames),\n        \"Get a list of all the module names loaded in the process memory.\");\n\n  m.def(\n      \"alignedAlloc\",\n      [](size_t size, size_t align) {\n        return reinterpret_cast<rword>(alignedAlloc(size, align));\n      },\n      \"Allocate a block of memory of a specified sized with an aligned base \"\n      \"address.\",\n      \"size\"_a, \"align\"_a);\n\n  m.def(\n      \"alignedFree\",\n      [](rword ptr) { alignedFree(reinterpret_cast<void *>(ptr)); },\n      \"Free a block of aligned memory allocated with alignedAlloc.\", \"ptr\"_a);\n\n  m.def(\n      \"allocateVirtualStack\",\n      [](GPRState *gprstate, uint32_t size) {\n        uint8_t *a = NULL;\n        if (allocateVirtualStack(gprstate, size, &a)) {\n          return static_cast<py::object>(py::int_(reinterpret_cast<rword>(a)));\n        } else {\n          return static_cast<py::object>(py::none());\n        }\n      },\n      \"Allocate a new stack and setup the GPRState accordingly.\\n\"\n      \"The allocated stack needs to be freed with alignedFree().\\n\"\n      \"The result was int, or None if the allocation fails.\",\n      \"gprstate\"_a, \"size\"_a);\n\n  m.def(\"simulateCall\", &simulateCall,\n        \"Simulate a call by modifying the stack and registers accordingly.\",\n        \"ctx\"_a, \"returnAddress\"_a, \"args\"_a = std::vector<rword>());\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/Range.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Range(py::module_ &m) {\n  py::class_<Range<rword>>(m, \"Range\")\n      .def(py::init<rword, rword>(), \"Create a new range\", \"start\"_a, \"end\"_a)\n\n      .def_property(\"start\", &Range<rword>::start, &Range<rword>::setStart,\n                    \"Range start value.\")\n\n      .def_property(\"end\", &Range<rword>::end, &Range<rword>::setEnd,\n                    \"Range end value (always excluded).\")\n\n      .def(\"size\", &Range<rword>::size, \"Return the total length of a range.\")\n\n      .def(py::self == py::self,\n           \"Return True if two ranges are equal (same boundaries).\", \"r\"_a)\n\n      .def(\"contains\",\n           (bool(Range<rword>::*)(const rword) const) & Range<rword>::contains,\n           \"Return True if an value is inside current range boundaries.\", \"t\"_a)\n\n      .def(\"contains\",\n           (bool(Range<rword>::*)(const Range<rword> &) const) &\n               Range<rword>::contains,\n           \"Return True if a range is inside current range boundaries.\", \"r\"_a)\n\n      .def(\"__contains__\",\n           (bool(Range<rword>::*)(const rword) const) & Range<rword>::contains,\n           \"Return True if an value is inside current range boundaries.\", \"t\"_a)\n\n      .def(\"__contains__\",\n           (bool(Range<rword>::*)(const Range<rword> &) const) &\n               Range<rword>::contains,\n           \"Return True if a range is inside current range boundaries.\", \"r\"_a)\n\n      .def(\"overlaps\", &Range<rword>::overlaps,\n           \"Return True if a range is overlapping current range lower or/and \"\n           \"upper boundary.\",\n           \"r\"_a)\n\n      .def(\"__str__\",\n           [](const Range<rword> &r) {\n             std::ostringstream oss;\n             r.display(oss);\n             return oss.str();\n           })\n      .def(\"__repr__\",\n           [](const Range<rword> &r) {\n             std::ostringstream oss;\n             r.display(oss);\n             return \"<Range \" + oss.str() + \">\";\n           })\n      .def(\"intersect\", &Range<rword>::intersect,\n           \"Return the intersection of two ranges.\", \"r\"_a)\n      .def(\"__getitem__\",\n           [](const Range<rword> &r, int index) {\n             switch (index) {\n               case 0:\n                 return r.start();\n               case 1:\n                 return r.end();\n               default:\n                 throw std::out_of_range(\"Only two elements\");\n             }\n           })\n      .def(\"__setitem__\",\n           [](Range<rword> &r, int index, rword value) {\n             switch (index) {\n               case 0:\n                 r.setStart(value);\n               case 1:\n                 r.setEnd(value);\n               default:\n                 throw std::out_of_range(\"Only two elements\");\n             }\n           })\n      .def(\"__copy__\", [](const Range<rword> &r) -> Range<rword> { return r; })\n      .def(py::pickle(\n          [](const Range<rword> &r) { // __getstate__\n            return py::make_tuple(r.start(), r.end());\n          },\n          [](py::tuple t) -> Range<rword> { // __setstate__\n            if (t.size() != 2)\n              throw std::runtime_error(\"Invalid state!\");\n\n            return {t[0].cast<rword>(), t[1].cast<rword>(),\n                    QBDI::real_addr_t()};\n          }));\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/VM.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"callback_python.h\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\n// Callback to python managment\ntemplate <typename T>\nstruct TrampData {\npublic:\n  T cbk;\n  py::object obj;\n  uint32_t id;\n\n  TrampData(const T &cbk, const py::object &obj) : cbk(cbk), obj(obj), id(0) {}\n};\n\ntemplate <typename T>\nstatic void removeTrampData(uint32_t n, std::map<uint32_t, T> &map) {\n  auto it = map.find(n);\n  if (it != map.end()) {\n    map.erase(it);\n  }\n}\n\ntemplate <typename T>\nstatic py::object\naddTrampData(uint32_t n, std::map<uint32_t, std::unique_ptr<TrampData<T>>> &map,\n             std::unique_ptr<TrampData<T>> data) {\n  if (n == VMError::INVALID_EVENTID) {\n    return py::cast(VMError::INVALID_EVENTID);\n  }\n  map[n] = std::move(data);\n  return py::cast(n);\n}\n\n// Map of python callback <=> QBDI number\nstatic std::map<uint32_t, std::unique_ptr<TrampData<PyInstCallback>>>\n    InstCallbackMap;\nstatic std::map<uint32_t, std::unique_ptr<TrampData<PyVMCallback>>>\n    VMCallbackMap;\nstatic std::map<uint32_t, std::unique_ptr<TrampData<PyInstrRuleCallback>>>\n    InstrRuleCallbackMap;\nstatic std::map<uint32_t,\n                std::vector<std::unique_ptr<TrampData<PyInstCallback>>>>\n    InstrumentInstCallbackMap;\n\nstatic void clearTrampDataMap() {\n  InstCallbackMap.clear();\n  VMCallbackMap.clear();\n  InstrRuleCallbackMap.clear();\n  InstrumentInstCallbackMap.clear();\n}\n\n// QBDI trampoline for python callback\nstatic VMAction trampoline_InstCallback(VMInstanceRef vm, GPRState *gprState,\n                                        FPRState *fprState, void *data) {\n  TrampData<PyInstCallback> *cbk =\n      static_cast<TrampData<PyInstCallback> *>(data);\n  VMAction res;\n  try {\n    res = cbk->cbk(vm, gprState, fprState, cbk->obj);\n  } catch (const std::exception &e) {\n    std::cerr << \"Error during InstCallback : \" << e.what() << std::endl;\n    exit(1);\n  }\n  return res;\n}\n\nstatic VMAction trampoline_VMCallback(VMInstanceRef vm, const VMState *vmState,\n                                      GPRState *gprState, FPRState *fprState,\n                                      void *data) {\n  TrampData<PyVMCallback> *cbk = static_cast<TrampData<PyVMCallback> *>(data);\n  VMAction res;\n  try {\n    res = cbk->cbk(vm, vmState, gprState, fprState, cbk->obj);\n  } catch (const std::exception &e) {\n    std::cerr << \"Error during VMCallback : \" << e.what() << std::endl;\n    exit(1);\n  }\n  return res;\n}\n\nstatic std::vector<InstrRuleDataCBK>\ntrampoline_InstrRuleCallback(VMInstanceRef vm, const InstAnalysis *analysis,\n                             void *data) {\n  TrampData<PyInstrRuleCallback> *cbk =\n      static_cast<TrampData<PyInstrRuleCallback> *>(data);\n  std::vector<InstrRuleDataCBKPython> resCB;\n  try {\n    resCB = cbk->cbk(vm, analysis, cbk->obj);\n  } catch (const std::exception &e) {\n    std::cerr << \"Error during InstrRuleCallback : \" << e.what() << std::endl;\n    exit(1);\n  }\n  std::vector<InstrRuleDataCBK> res;\n  if (resCB.size() == 0) {\n    return res;\n  }\n  if (InstrumentInstCallbackMap.count(cbk->id) == 0) {\n    InstrumentInstCallbackMap[cbk->id] =\n        std::vector<std::unique_ptr<TrampData<PyInstCallback>>>{};\n  }\n  auto it = InstrumentInstCallbackMap.find(cbk->id);\n  assert(it == InstrumentInstCallbackMap.end());\n  auto &vec = it->second;\n\n  for (const InstrRuleDataCBKPython &cb : resCB) {\n    std::unique_ptr<TrampData<PyInstCallback>> data{\n        new TrampData<PyInstCallback>(cb.cbk, cb.data)};\n    data->id = cbk->id;\n    res.emplace_back(cb.position, trampoline_InstCallback,\n                     static_cast<void *>(data.get()), cb.priority);\n    vec.push_back(std::move(data));\n  }\n\n  return res;\n}\n\nvoid init_binding_VM(py::module_ &m) {\n\n  py::module_ atexit = py::module_::import(\"atexit\");\n  atexit.attr(\"register\")(std::function<void()>(clearTrampDataMap));\n\n  py::class_<VM>(m, \"VM\")\n      .def(py::init<const std::string &, const std::vector<std::string> &,\n                    Options>(),\n           \"Construct a new VM for a given CPU with specific attributes\",\n           \"cpu\"_a = \"\", \"mattrs\"_a = std::vector<std::string>(),\n           \"options\"_a = NO_OPT)\n      .def_property(\"options\", &VM::getOptions, &VM::setOptions,\n                    \"Options of the VM\")\n      .def(\"getGPRState\", &VM::getGPRState,\n           py::return_value_policy::reference_internal,\n           \"Obtain the current general purpose register state.\")\n      .def(\"getFPRState\", &VM::getFPRState,\n           py::return_value_policy::reference_internal,\n           \"Obtain the current floating point register state.\")\n      .def(\"getErrno\", &VM::getErrno, \"Get the backuped errno.\")\n      .def(\"setGPRState\", &VM::setGPRState, \"Set the GPR state.\", \"gprState\"_a)\n      .def(\"setFPRState\", &VM::setFPRState, \"Set the FPR state.\", \"fprState\"_a)\n      .def(\"setErrno\", &VM::setErrno, \"Set the backuped errno.\", \"errno\"_a)\n      .def(\"addInstrumentedRange\", &VM::addInstrumentedRange,\n           \"Add an address range to the set of instrumented address ranges.\",\n           \"start\"_a, \"end\"_a)\n      .def(\"addInstrumentedModule\", &VM::addInstrumentedModule,\n           \"Add the executable address ranges of a module to the set of \"\n           \"instrumented address ranges.\",\n           \"name\"_a)\n      .def(\"addInstrumentedModuleFromAddr\", &VM::addInstrumentedModuleFromAddr,\n           \"Add the executable address ranges of a module to the set of \"\n           \"instrumented address ranges \"\n           \"using an address belonging to the module.\",\n           \"addr\"_a)\n      .def(\"instrumentAllExecutableMaps\", &VM::instrumentAllExecutableMaps,\n           \"Adds all the executable memory maps to the instrumented range set.\")\n      .def(\"removeInstrumentedRange\", &VM::removeInstrumentedRange,\n           \"Remove an address range from the set of instrumented address \"\n           \"ranges.\",\n           \"start\"_a, \"end\"_a)\n      .def(\"removeInstrumentedModule\", &VM::removeInstrumentedModule,\n           \"Remove the executable address ranges of a module from the set of \"\n           \"instrumented address ranges.\",\n           \"name\"_a)\n      .def(\"removeInstrumentedModuleFromAddr\",\n           &VM::removeInstrumentedModuleFromAddr,\n           \"Remove the executable address ranges of a module from the set of \"\n           \"instrumented address ranges \"\n           \"using an address belonging to the module.\",\n           \"addr\"_a)\n      .def(\"removeAllInstrumentedRanges\", &VM::removeAllInstrumentedRanges,\n           \"Remove all instrumented ranges.\")\n      .def(\"run\", &VM::run, \"Start the execution by the DBI.\", \"start\"_a,\n           \"stop\"_a)\n      .def(\n          \"call\",\n          [](VM &vm, rword function, std::vector<rword> &args) {\n            rword retvalue;\n            bool ret = vm.call(&retvalue, function, args);\n            return std::make_tuple(ret, retvalue);\n          },\n          \"Call a function using the DBI (and its current state).\",\n          \"function\"_a, \"args\"_a)\n      .def(\n          \"addInstrRule\",\n          [](VM &vm, PyInstrRuleCallback &cbk, AnalysisType type,\n             py::object &obj) {\n            std::unique_ptr<TrampData<PyInstrRuleCallback>> data{\n                new TrampData<PyInstrRuleCallback>(cbk, obj)};\n            uint32_t n = vm.addInstrRule(&trampoline_InstrRuleCallback, type,\n                                         static_cast<void *>(data.get()));\n            data->id = n;\n            return addTrampData(n, InstrRuleCallbackMap, std::move(data));\n          },\n          \"Add a custom instrumentation rule to the VM.\", \"cbk\"_a, \"type\"_a,\n          \"data\"_a)\n      .def(\n          \"addInstrRuleRange\",\n          [](VM &vm, rword start, rword end, PyInstrRuleCallback &cbk,\n             AnalysisType type, py::object &obj) {\n            std::unique_ptr<TrampData<PyInstrRuleCallback>> data{\n                new TrampData<PyInstrRuleCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addInstrRuleRange(start, end, &trampoline_InstrRuleCallback,\n                                     type, static_cast<void *>(data.get()));\n            data->id = n;\n            return addTrampData(n, InstrRuleCallbackMap, std::move(data));\n          },\n          \"Add a custom instrumentation rule to the VM on a specify range.\",\n          \"start\"_a, \"end\"_a, \"cbk\"_a, \"type\"_a, \"data\"_a)\n      .def(\n          \"addMnemonicCB\",\n          [](VM &vm, const char *mnemonic, InstPosition pos,\n             PyInstCallback &cbk, py::object &obj, int priority) {\n            std::unique_ptr<TrampData<PyInstCallback>> data{\n                new TrampData<PyInstCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addMnemonicCB(mnemonic, pos, &trampoline_InstCallback,\n                                 static_cast<void *>(data.get()), priority);\n            data->id = n;\n            return addTrampData(n, InstCallbackMap, std::move(data));\n          },\n          \"Register a callback event if the instruction matches the mnemonic.\",\n          \"mnemonic\"_a, \"pos\"_a, \"cbk\"_a, \"data\"_a,\n          \"priority\"_a = PRIORITY_DEFAULT)\n      .def(\n          \"addCodeCB\",\n          [](VM &vm, InstPosition pos, PyInstCallback &cbk, py::object &obj,\n             int priority) {\n            std::unique_ptr<TrampData<PyInstCallback>> data{\n                new TrampData<PyInstCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addCodeCB(pos, &trampoline_InstCallback,\n                             static_cast<void *>(data.get()), priority);\n            data->id = n;\n            return addTrampData(n, InstCallbackMap, std::move(data));\n          },\n          \"Register a callback event for every instruction executed.\", \"pos\"_a,\n          \"cbk\"_a, \"data\"_a, \"priority\"_a = PRIORITY_DEFAULT)\n      .def(\n          \"addCodeAddrCB\",\n          [](VM &vm, rword address, InstPosition pos, PyInstCallback &cbk,\n             py::object &obj, int priority) {\n            std::unique_ptr<TrampData<PyInstCallback>> data{\n                new TrampData<PyInstCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addCodeAddrCB(address, pos, &trampoline_InstCallback,\n                                 static_cast<void *>(data.get()), priority);\n            data->id = n;\n            return addTrampData(n, InstCallbackMap, std::move(data));\n          },\n          \"Register a callback for when a specific address is executed.\",\n          \"address\"_a, \"pos\"_a, \"cbk\"_a, \"data\"_a,\n          \"priority\"_a = PRIORITY_DEFAULT)\n      .def(\n          \"addCodeRangeCB\",\n          [](VM &vm, rword start, rword end, InstPosition pos,\n             PyInstCallback &cbk, py::object &obj, int priority) {\n            std::unique_ptr<TrampData<PyInstCallback>> data{\n                new TrampData<PyInstCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addCodeRangeCB(start, end, pos, &trampoline_InstCallback,\n                                  static_cast<void *>(data.get()), priority);\n            data->id = n;\n            return addTrampData(n, InstCallbackMap, std::move(data));\n          },\n          \"Register a callback for when a specific address range is executed.\",\n          \"start\"_a, \"end\"_a, \"pos\"_a, \"cbk\"_a, \"data\"_a,\n          \"priority\"_a = PRIORITY_DEFAULT)\n      .def(\n          \"addMemAccessCB\",\n          [](VM &vm, MemoryAccessType type, PyInstCallback &cbk,\n             py::object &obj, int priority) {\n            std::unique_ptr<TrampData<PyInstCallback>> data{\n                new TrampData<PyInstCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addMemAccessCB(type, &trampoline_InstCallback,\n                                  static_cast<void *>(data.get()), priority);\n            data->id = n;\n            return addTrampData(n, InstCallbackMap, std::move(data));\n          },\n          \"Register a callback event for every memory access matching the type \"\n          \"bitfield made by the instructions.\",\n          \"type\"_a, \"cbk\"_a, \"data\"_a, \"priority\"_a = PRIORITY_DEFAULT)\n      .def(\n          \"addMemAddrCB\",\n          [](VM &vm, rword address, MemoryAccessType type, PyInstCallback &cbk,\n             py::object &obj) {\n            std::unique_ptr<TrampData<PyInstCallback>> data{\n                new TrampData<PyInstCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addMemAddrCB(address, type, &trampoline_InstCallback,\n                                static_cast<void *>(data.get()));\n            return addTrampData(n, InstCallbackMap, std::move(data));\n          },\n          \"Add a virtual callback which is triggered for any memory access at \"\n          \"a specific address \"\n          \"matching the access type. Virtual callbacks are called via callback \"\n          \"forwarding by a \"\n          \"gate callback triggered on every memory access. This incurs a high \"\n          \"performance cost.\",\n          \"address\"_a, \"type\"_a, \"cbk\"_a, \"data\"_a)\n      .def(\n          \"addMemRangeCB\",\n          [](VM &vm, rword start, rword end, MemoryAccessType type,\n             PyInstCallback &cbk, py::object &obj) {\n            std::unique_ptr<TrampData<PyInstCallback>> data{\n                new TrampData<PyInstCallback>(cbk, obj)};\n            uint32_t n =\n                vm.addMemRangeCB(start, end, type, &trampoline_InstCallback,\n                                 static_cast<void *>(data.get()));\n            data->id = n;\n            return addTrampData(n, InstCallbackMap, std::move(data));\n          },\n          \"Add a virtual callback which is triggered for any memory access at \"\n          \"a specific address range \"\n          \"matching the access type. Virtual callbacks are called via callback \"\n          \"forwarding by a \"\n          \"gate callback triggered on every memory access. This incurs a high \"\n          \"performance cost.\",\n          \"start\"_a, \"end\"_a, \"type\"_a, \"cbk\"_a, \"data\"_a)\n      .def(\n          \"addVMEventCB\",\n          [](VM &vm, VMEvent mask, PyVMCallback &cbk, py::object &obj) {\n            std::unique_ptr<TrampData<PyVMCallback>> data{\n                new TrampData<PyVMCallback>(cbk, obj)};\n            uint32_t n = vm.addVMEventCB(mask, &trampoline_VMCallback,\n                                         static_cast<void *>(data.get()));\n            data->id = n;\n            return addTrampData(n, VMCallbackMap, std::move(data));\n          },\n          \"Register a callback event for a specific VM event.\", \"mask\"_a,\n          \"cbk\"_a, \"data\"_a)\n      .def(\n          \"deleteInstrumentation\",\n          [](VM &vm, uint32_t id) {\n            vm.deleteInstrumentation(id);\n            removeTrampData(id, InstCallbackMap);\n            removeTrampData(id, VMCallbackMap);\n            removeTrampData(id, InstrRuleCallbackMap);\n            removeTrampData(id, InstrumentInstCallbackMap);\n          },\n          \"Remove an instrumentation.\", \"id\"_a)\n      .def(\n          \"deleteAllInstrumentations\",\n          [](VM &vm) {\n            vm.deleteAllInstrumentations();\n            clearTrampDataMap();\n          },\n          \"Remove all the registered instrumentations.\")\n      .def(\n          \"getInstAnalysis\",\n          [](const VM &vm, AnalysisType type) {\n            return vm.getInstAnalysis(type);\n          },\n          \"Obtain the analysis of the current instruction. Analysis results \"\n          \"are cached in the VM.\",\n          py::arg_v(\"type\",\n                    AnalysisType::ANALYSIS_INSTRUCTION |\n                        AnalysisType::ANALYSIS_DISASSEMBLY,\n                    \"AnalysisType.ANALYSIS_INSTRUCTION|AnalysisType.ANALYSIS_\"\n                    \"DISASSEMBLY\"),\n          py::return_value_policy::copy)\n      .def(\n          \"getJITInstAnalysis\",\n          [](const VM &vm, rword address, AnalysisType type) {\n            return vm.getJITInstAnalysis(address, type);\n          },\n          \"Obtain the analysis of a JITed instruction. Analysis results are \"\n          \"cached in the VM. This API may be used to determine if a given \"\n          \"address of the current process memory correspond to the JIT patch \"\n          \"from this VM.\",\n          \"address\"_a,\n          py::arg_v(\"type\",\n                    AnalysisType::ANALYSIS_INSTRUCTION |\n                        AnalysisType::ANALYSIS_DISASSEMBLY,\n                    \"AnalysisType.ANALYSIS_INSTRUCTION|AnalysisType.ANALYSIS_\"\n                    \"DISASSEMBLY\"),\n          py::return_value_policy::copy)\n      .def(\n          \"getCachedInstAnalysis\",\n          [](const VM &vm, rword address, AnalysisType type) {\n            return vm.getCachedInstAnalysis(address, type);\n          },\n          \"Obtain the analysis of a cached instruction. Analysis results are \"\n          \"cached in the VM.\",\n          \"address\"_a,\n          py::arg_v(\"type\",\n                    AnalysisType::ANALYSIS_INSTRUCTION |\n                        AnalysisType::ANALYSIS_DISASSEMBLY |\n                        AnalysisType::ANALYSIS_JIT,\n                    \"AnalysisType.ANALYSIS_INSTRUCTION|AnalysisType.ANALYSIS_\"\n                    \"DISASSEMBLY|AnalysisType.ANALYSIS_JIT\"),\n          py::return_value_policy::copy)\n      .def(\"recordMemoryAccess\", &VM::recordMemoryAccess,\n           \"Add instrumentation rules to log memory access using inline \"\n           \"instrumentation and instruction shadows.\",\n           \"type\"_a)\n      .def(\"getInstMemoryAccess\", &VM::getInstMemoryAccess,\n           \"Obtain the memory accesses made by the last executed instruction.\",\n           py::return_value_policy::copy)\n      .def(\"getBBMemoryAccess\", &VM::getBBMemoryAccess,\n           \"Obtain the memory accesses made by the last executed sequence.\",\n           py::return_value_policy::copy)\n      .def(\"precacheBasicBlock\", &VM::precacheBasicBlock,\n           \"Pre-cache a known basic block\", \"pc\"_a)\n      .def(\"clearCache\", &VM::clearCache,\n           \"Clear a specific address range from the translation cache.\",\n           \"start\"_a, \"end\"_a)\n      .def(\"clearAllCache\", &VM::clearAllCache,\n           \"Clear the entire translation cache.\")\n      .def(\"getNbExecBlock\", &VM::getNbExecBlock,\n           \"Get the number of ExecBlock in the cache. Each block uses 2 memory \"\n           \"pages and some heap allocations.\")\n      .def(\"reduceCacheTo\", &VM::reduceCacheTo,\n           \"Reduce the cache to X ExecBlock.\", \"nb\"_a);\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/X86/CMakeLists.txt",
    "content": "target_sources(\n  pyqbdi_module INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/State_X86.cpp\"\n                          \"${CMAKE_CURRENT_LIST_DIR}/Options_X86.cpp\")\n"
  },
  {
    "path": "tools/pyqbdi/binding/X86/Options_X86.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Enum.hpp\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Options(py::module_ &m) {\n\n  enum_int_flag_<Options>(m, \"Options\", \"VM options\", py::arithmetic())\n      .value(\"NO_OPT\", Options::NO_OPT, \"Default value\")\n      .value(\"OPT_DISABLE_FPR\", Options::OPT_DISABLE_FPR,\n             \"Disable all operation on FPU (SSE, AVX, SIMD). May break the \"\n             \"execution if the target use the FPU.\")\n      .value(\"OPT_DISABLE_OPTIONAL_FPR\", Options::OPT_DISABLE_OPTIONAL_FPR,\n             \"Disable context switch optimisation when the target execblock \"\n             \"doesn't used FPR\")\n      .value(\"OPT_DISABLE_MEMORYACCESS_VALUE\",\n             Options::OPT_DISABLE_MEMORYACCESS_VALUE,\n             \"Don't load memory access value\")\n      .value(\"OPT_DISABLE_ERRNO_BACKUP\", Options::OPT_DISABLE_ERRNO_BACKUP,\n             \"Don't save and restore errno\")\n      .value(\"OPT_ATT_SYNTAX\", Options::OPT_ATT_SYNTAX,\n             \"Used the AT&T syntax for instruction disassembly\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/X86/State_X86.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid hexify_register(std::ostringstream &out, const char *s, int size) {\n  for (int i = size - 1; i >= 0; i--) {\n    out << std::setw(2) << (static_cast<unsigned>(s[i]) & 0xff);\n  }\n}\n\nvoid init_binding_State(py::module_ &m) {\n\n  py::class_<FPControl>(m, \"FPControl\")\n      .def(py::init<>())\n      .def_property(\n          \"invalid\", [](const FPControl &t) { return t.invalid; },\n          [](FPControl &t, int v) { t.invalid = v & 1; })\n      .def_property(\n          \"denorm\", [](const FPControl &t) { return t.denorm; },\n          [](FPControl &t, int v) { t.denorm = v & 1; })\n      .def_property(\n          \"zdiv\", [](const FPControl &t) { return t.zdiv; },\n          [](FPControl &t, int v) { t.zdiv = v & 1; })\n      .def_property(\n          \"ovrfl\", [](const FPControl &t) { return t.ovrfl; },\n          [](FPControl &t, int v) { t.ovrfl = v & 1; })\n      .def_property(\n          \"undfl\", [](const FPControl &t) { return t.undfl; },\n          [](FPControl &t, int v) { t.undfl = v & 1; })\n      .def_property(\n          \"precis\", [](const FPControl &t) { return t.precis; },\n          [](FPControl &t, int v) { t.precis = v & 1; })\n      .def_property(\n          \"pc\", [](const FPControl &t) { return t.pc; },\n          [](FPControl &t, int v) { t.pc = v & 3; })\n      .def_property(\n          \"rc\", [](const FPControl &t) { return t.rc; },\n          [](FPControl &t, int v) { t.rc = v & 3; });\n\n  py::class_<FPStatus>(m, \"FPStatus\")\n      .def(py::init<>())\n      .def_property(\n          \"invalid\", [](const FPStatus &t) { return t.invalid; },\n          [](FPStatus &t, int v) { t.invalid = v & 1; })\n      .def_property(\n          \"denorm\", [](const FPStatus &t) { return t.denorm; },\n          [](FPStatus &t, int v) { t.denorm = v & 1; })\n      .def_property(\n          \"zdiv\", [](const FPStatus &t) { return t.zdiv; },\n          [](FPStatus &t, int v) { t.zdiv = v & 1; })\n      .def_property(\n          \"ovrfl\", [](const FPStatus &t) { return t.ovrfl; },\n          [](FPStatus &t, int v) { t.ovrfl = v & 1; })\n      .def_property(\n          \"undfl\", [](const FPStatus &t) { return t.undfl; },\n          [](FPStatus &t, int v) { t.undfl = v & 1; })\n      .def_property(\n          \"precis\", [](const FPStatus &t) { return t.precis; },\n          [](FPStatus &t, int v) { t.precis = v & 1; })\n      .def_property(\n          \"stkflt\", [](const FPStatus &t) { return t.stkflt; },\n          [](FPStatus &t, int v) { t.stkflt = v & 1; })\n      .def_property(\n          \"errsumm\", [](const FPStatus &t) { return t.errsumm; },\n          [](FPStatus &t, int v) { t.errsumm = v & 1; })\n      .def_property(\n          \"c0\", [](const FPStatus &t) { return t.c0; },\n          [](FPStatus &t, int v) { t.c0 = v & 1; })\n      .def_property(\n          \"c1\", [](const FPStatus &t) { return t.c1; },\n          [](FPStatus &t, int v) { t.c1 = v & 1; })\n      .def_property(\n          \"c2\", [](const FPStatus &t) { return t.c2; },\n          [](FPStatus &t, int v) { t.c2 = v & 1; })\n      .def_property(\n          \"tos\", [](const FPStatus &t) { return t.tos; },\n          [](FPStatus &t, int v) { t.tos = v & 7; })\n      .def_property(\n          \"c3\", [](const FPStatus &t) { return t.c3; },\n          [](FPStatus &t, int v) { t.c3 = v & 1; })\n      .def_property(\n          \"busy\", [](const FPStatus &t) { return t.busy; },\n          [](FPStatus &t, int v) { t.busy = v & 1; });\n\n  py::class_<MMSTReg>(m, \"MMSTReg\")\n      .def(py::init<>())\n      .def_property(\n          \"st\",\n          [](const MMSTReg &t) { return py::bytes(t.reg, sizeof(t.reg)); },\n          [](MMSTReg &t, py::bytes v) {\n            std::string(v).copy(t.reg, sizeof(t.reg), 0);\n          });\n\n  py::class_<FPRState>(m, \"FPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"fcw\", &FPRState::fcw, \"x87 FPU control word\")\n      .def_readwrite(\"rfcw\", &FPRState::rfcw, \"x87 FPU control word\")\n      .def_readwrite(\"fsw\", &FPRState::fsw, \"x87 FPU status word\")\n      .def_readwrite(\"rfsw\", &FPRState::rfsw, \"x87 FPU status word\")\n      .def_readwrite(\"ftw\", &FPRState::ftw, \"x87 FPU tag word\")\n      .def_readwrite(\"fop\", &FPRState::fop, \"x87 FPU Opcode\")\n      .def_readwrite(\"ip\", &FPRState::ip, \"x87 FPU Instruction Pointer offset\")\n      .def_readwrite(\"cs\", &FPRState::cs,\n                     \"x87 FPU Instruction Pointer Selector\")\n      .def_readwrite(\"dp\", &FPRState::dp,\n                     \"x87 FPU Instruction Operand(Data) Pointer offset\")\n      .def_readwrite(\"ds\", &FPRState::ds,\n                     \"x87 FPU Instruction Operand(Data) Pointer Selector\")\n      .def_readwrite(\"mxcsr\", &FPRState::mxcsr, \"MXCSR Register state\")\n      .def_readwrite(\"mxcsrmask\", &FPRState::mxcsrmask, \"MXCSR mask\")\n      .def_readwrite(\"stmm0\", &FPRState::stmm0, \"ST0/MM0\")\n      .def_readwrite(\"stmm1\", &FPRState::stmm1, \"ST1/MM1\")\n      .def_readwrite(\"stmm2\", &FPRState::stmm2, \"ST2/MM2\")\n      .def_readwrite(\"stmm3\", &FPRState::stmm3, \"ST3/MM3\")\n      .def_readwrite(\"stmm4\", &FPRState::stmm4, \"ST4/MM4\")\n      .def_readwrite(\"stmm5\", &FPRState::stmm5, \"ST5/MM5\")\n      .def_readwrite(\"stmm6\", &FPRState::stmm6, \"ST6/MM6\")\n      .def_readwrite(\"stmm7\", &FPRState::stmm7, \"ST7/MM7\")\n      .def_property(\n          \"xmm0\",\n          [](const FPRState &t) { return py::bytes(t.xmm0, sizeof(t.xmm0)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm0, sizeof(t.xmm0), 0);\n          },\n          \"XMM 0\")\n      .def_property(\n          \"xmm1\",\n          [](const FPRState &t) { return py::bytes(t.xmm1, sizeof(t.xmm1)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm1, sizeof(t.xmm1), 0);\n          },\n          \"XMM 1\")\n      .def_property(\n          \"xmm2\",\n          [](const FPRState &t) { return py::bytes(t.xmm2, sizeof(t.xmm2)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm2, sizeof(t.xmm2), 0);\n          },\n          \"XMM 2\")\n      .def_property(\n          \"xmm3\",\n          [](const FPRState &t) { return py::bytes(t.xmm3, sizeof(t.xmm3)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm3, sizeof(t.xmm3), 0);\n          },\n          \"XMM 3\")\n      .def_property(\n          \"xmm4\",\n          [](const FPRState &t) { return py::bytes(t.xmm4, sizeof(t.xmm4)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm4, sizeof(t.xmm4), 0);\n          },\n          \"XMM 4\")\n      .def_property(\n          \"xmm5\",\n          [](const FPRState &t) { return py::bytes(t.xmm5, sizeof(t.xmm5)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm5, sizeof(t.xmm5), 0);\n          },\n          \"XMM 5\")\n      .def_property(\n          \"xmm6\",\n          [](const FPRState &t) { return py::bytes(t.xmm6, sizeof(t.xmm6)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm6, sizeof(t.xmm6), 0);\n          },\n          \"XMM 6\")\n      .def_property(\n          \"xmm7\",\n          [](const FPRState &t) { return py::bytes(t.xmm7, sizeof(t.xmm7)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm7, sizeof(t.xmm7), 0);\n          },\n          \"XMM 7\")\n      .def_property(\n          \"ymm0\",\n          [](const FPRState &t) { return py::bytes(t.ymm0, sizeof(t.ymm0)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm0, sizeof(t.ymm0), 0);\n          },\n          \"YMM0[255:128]\")\n      .def_property(\n          \"ymm1\",\n          [](const FPRState &t) { return py::bytes(t.ymm1, sizeof(t.ymm1)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm1, sizeof(t.ymm1), 0);\n          },\n          \"YMM1[255:128]\")\n      .def_property(\n          \"ymm2\",\n          [](const FPRState &t) { return py::bytes(t.ymm2, sizeof(t.ymm2)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm2, sizeof(t.ymm2), 0);\n          },\n          \"YMM2[255:128]\")\n      .def_property(\n          \"ymm3\",\n          [](const FPRState &t) { return py::bytes(t.ymm3, sizeof(t.ymm3)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm3, sizeof(t.ymm3), 0);\n          },\n          \"YMM3[255:128]\")\n      .def_property(\n          \"ymm4\",\n          [](const FPRState &t) { return py::bytes(t.ymm4, sizeof(t.ymm4)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm4, sizeof(t.ymm4), 0);\n          },\n          \"YMM4[255:128]\")\n      .def_property(\n          \"ymm5\",\n          [](const FPRState &t) { return py::bytes(t.ymm5, sizeof(t.ymm5)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm5, sizeof(t.ymm5), 0);\n          },\n          \"YMM5[255:128]\")\n      .def_property(\n          \"ymm6\",\n          [](const FPRState &t) { return py::bytes(t.ymm6, sizeof(t.ymm6)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm6, sizeof(t.ymm6), 0);\n          },\n          \"YMM6[255:128]\")\n      .def_property(\n          \"ymm7\",\n          [](const FPRState &t) { return py::bytes(t.ymm7, sizeof(t.ymm7)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm7, sizeof(t.ymm7), 0);\n          },\n          \"YMM7[255:128]\")\n      .def(\"__str__\",\n           [](const FPRState &obj) {\n             std::ostringstream oss;\n             oss << std::hex << std::setfill('0')\n                 << \"=== FPRState begin ===\" << std::endl\n                 << \"rfcw  : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.rfcw\n                 << std::endl\n                 << \"rfsw  : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.rfsw\n                 << std::endl\n                 << \"ftw   : 0x\" << std::setw(sizeof(uint8_t) * 2)\n                 << (static_cast<unsigned>(obj.ftw) & 0xff) << std::endl\n                 << \"fop   : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.fop\n                 << std::endl\n                 << \"ip    : 0x\" << std::setw(sizeof(uint32_t) * 2) << obj.ip\n                 << std::endl\n                 << \"cs    : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.cs\n                 << std::endl\n                 << \"dp    : 0x\" << std::setw(sizeof(uint32_t) * 2) << obj.dp\n                 << std::endl\n                 << \"ds    : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.ds\n                 << std::endl\n                 << \"mxcsr : 0x\" << std::setw(sizeof(uint32_t) * 2) << obj.mxcsr\n                 << std::endl\n                 << \"mxcsrmask : 0x\" << std::setw(sizeof(uint32_t) * 2)\n                 << obj.mxcsrmask << std::endl\n                 << \"stmm0 : 0x\";\n             hexify_register(oss, obj.stmm0.reg, sizeof(obj.stmm0.reg));\n             oss << std::endl << \"stmm1 : 0x\";\n             hexify_register(oss, obj.stmm1.reg, sizeof(obj.stmm1.reg));\n             oss << std::endl << \"stmm2 : 0x\";\n             hexify_register(oss, obj.stmm2.reg, sizeof(obj.stmm2.reg));\n             oss << std::endl << \"stmm3 : 0x\";\n             hexify_register(oss, obj.stmm3.reg, sizeof(obj.stmm3.reg));\n             oss << std::endl << \"stmm4 : 0x\";\n             hexify_register(oss, obj.stmm4.reg, sizeof(obj.stmm4.reg));\n             oss << std::endl << \"stmm5 : 0x\";\n             hexify_register(oss, obj.stmm5.reg, sizeof(obj.stmm5.reg));\n             oss << std::endl << \"stmm6 : 0x\";\n             hexify_register(oss, obj.stmm6.reg, sizeof(obj.stmm6.reg));\n             oss << std::endl << \"stmm7 : 0x\";\n             hexify_register(oss, obj.stmm7.reg, sizeof(obj.stmm7.reg));\n\n             oss << std::endl << \"xmm0  : 0x\";\n             hexify_register(oss, obj.xmm0, sizeof(obj.xmm0));\n             oss << std::endl << \"xmm1  : 0x\";\n             hexify_register(oss, obj.xmm1, sizeof(obj.xmm1));\n             oss << std::endl << \"xmm2  : 0x\";\n             hexify_register(oss, obj.xmm2, sizeof(obj.xmm2));\n             oss << std::endl << \"xmm3  : 0x\";\n             hexify_register(oss, obj.xmm3, sizeof(obj.xmm3));\n             oss << std::endl << \"xmm4  : 0x\";\n             hexify_register(oss, obj.xmm4, sizeof(obj.xmm4));\n             oss << std::endl << \"xmm5  : 0x\";\n             hexify_register(oss, obj.xmm5, sizeof(obj.xmm5));\n             oss << std::endl << \"xmm6  : 0x\";\n             hexify_register(oss, obj.xmm6, sizeof(obj.xmm6));\n             oss << std::endl << \"xmm7  : 0x\";\n             hexify_register(oss, obj.xmm7, sizeof(obj.xmm7));\n\n             oss << std::endl << \"ymm0  : 0x\";\n             hexify_register(oss, obj.ymm0, sizeof(obj.ymm0));\n             hexify_register(oss, obj.xmm0, sizeof(obj.xmm0));\n             oss << std::endl << \"ymm1  : 0x\";\n             hexify_register(oss, obj.ymm1, sizeof(obj.ymm1));\n             hexify_register(oss, obj.xmm1, sizeof(obj.xmm1));\n             oss << std::endl << \"ymm2  : 0x\";\n             hexify_register(oss, obj.ymm2, sizeof(obj.ymm2));\n             hexify_register(oss, obj.xmm2, sizeof(obj.xmm2));\n             oss << std::endl << \"ymm3  : 0x\";\n             hexify_register(oss, obj.ymm3, sizeof(obj.ymm3));\n             hexify_register(oss, obj.xmm3, sizeof(obj.xmm3));\n             oss << std::endl << \"ymm4  : 0x\";\n             hexify_register(oss, obj.ymm4, sizeof(obj.ymm4));\n             hexify_register(oss, obj.xmm4, sizeof(obj.xmm4));\n             oss << std::endl << \"ymm5  : 0x\";\n             hexify_register(oss, obj.ymm5, sizeof(obj.ymm5));\n             hexify_register(oss, obj.xmm5, sizeof(obj.xmm5));\n             oss << std::endl << \"ymm6  : 0x\";\n             hexify_register(oss, obj.ymm6, sizeof(obj.ymm6));\n             hexify_register(oss, obj.xmm6, sizeof(obj.xmm6));\n             oss << std::endl << \"ymm7  : 0x\";\n             hexify_register(oss, obj.ymm7, sizeof(obj.ymm7));\n             hexify_register(oss, obj.xmm7, sizeof(obj.xmm7));\n\n             oss << std::endl << \"=== FPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\"__copy__\", [](const FPRState &state) -> FPRState { return state; })\n      .def(py::pickle(\n          [](const FPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"X86\", py::bytes(reinterpret_cast<const char *>(&state),\n                                 sizeof(FPRState)));\n          },\n          [](py::tuple t) -> FPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"X86\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"X86\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(FPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(FPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            FPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(FPRState));\n            return newState;\n\n          }));\n\n  m.attr(\"REG_RETURN\") = REG_RETURN;\n  m.attr(\"AVAILABLE_GPR\") = AVAILABLE_GPR;\n  m.attr(\"REG_BP\") = REG_BP;\n  m.attr(\"REG_SP\") = REG_SP;\n  m.attr(\"REG_PC\") = REG_PC;\n  m.attr(\"NUM_GPR\") = NUM_GPR;\n  m.attr(\"REG_LR\") = py::none();\n  m.attr(\"REG_FLAG\") = REG_FLAG;\n\n  py::class_<GPRState>(m, \"GPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"eax\", &GPRState::eax)\n      .def_readwrite(\"ebx\", &GPRState::ebx)\n      .def_readwrite(\"ecx\", &GPRState::ecx)\n      .def_readwrite(\"edx\", &GPRState::edx)\n      .def_readwrite(\"esi\", &GPRState::esi)\n      .def_readwrite(\"edi\", &GPRState::edi)\n      .def_readwrite(\"ebp\", &GPRState::ebp)\n      .def_readwrite(\"esp\", &GPRState::esp)\n      .def_readwrite(\"eip\", &GPRState::eip)\n      .def_readwrite(\"eflags\", &GPRState::eflags)\n      // cross architecture access\n      .def_readwrite(\"REG_RETURN\", &GPRState::eax, \"shadow of eax\")\n      .def_readwrite(\"AVAILABLE_GPR\", &GPRState::ebp, \"shadow of ebp\")\n      .def_readwrite(\"REG_BP\", &GPRState::ebp, \"shadow of ebp\")\n      .def_readwrite(\"REG_SP\", &GPRState::esp, \"shadow of esp\")\n      .def_readwrite(\"REG_PC\", &GPRState::eip, \"shadow of eip\")\n      .def_readwrite(\"NUM_GPR\", &GPRState::eflags, \"shadow of eflags\")\n      .def_readwrite(\"REG_FLAG\", &GPRState::eflags, \"shadow of eflags\")\n      .def_property_readonly(\n          \"REG_LR\", [](const GPRState &obj) { return py::none(); },\n          \"not available on X86\")\n      .def(\"__str__\",\n           [](const GPRState &obj) {\n             std::ostringstream oss;\n             int r = sizeof(rword) * 2;\n             oss << std::hex << std::setfill('0')\n                 << \"=== GPRState begin ===\" << std::endl\n                 << \"eax    : 0x\" << std::setw(r) << obj.eax << std::endl\n                 << \"ebx    : 0x\" << std::setw(r) << obj.ebx << std::endl\n                 << \"ecx    : 0x\" << std::setw(r) << obj.ecx << std::endl\n                 << \"edx    : 0x\" << std::setw(r) << obj.edx << std::endl\n                 << \"esi    : 0x\" << std::setw(r) << obj.esi << std::endl\n                 << \"edi    : 0x\" << std::setw(r) << obj.edi << std::endl\n                 << \"ebp    : 0x\" << std::setw(r) << obj.ebp << std::endl\n                 << \"esp    : 0x\" << std::setw(r) << obj.esp << std::endl\n                 << \"eip    : 0x\" << std::setw(r) << obj.eip << std::endl\n                 << \"eflags : 0x\" << std::setw(r) << obj.eflags << std::endl\n                 << \"=== GPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\n          \"__getitem__\",\n          [](const GPRState &obj, unsigned int index) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_GET(&obj, index);\n          },\n          \"Get a register like QBDI_GPR_GET\", \"index\"_a)\n      .def(\n          \"__setitem__\",\n          [](GPRState &obj, unsigned int index, rword value) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_SET(&obj, index, value);\n          },\n          \"Set a register like QBDI_GPR_SET\", \"index\"_a, \"value\"_a)\n      .def(\"__copy__\", [](const GPRState &state) -> GPRState { return state; })\n      .def(py::pickle(\n          [](const GPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"X86\", py::bytes(reinterpret_cast<const char *>(&state),\n                                 sizeof(GPRState)));\n          },\n          [](py::tuple t) -> GPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"X86\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"X86\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(GPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(GPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            GPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(GPRState));\n            return newState;\n\n          }));\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/X86_64/CMakeLists.txt",
    "content": "target_sources(\n  pyqbdi_module INTERFACE \"${CMAKE_CURRENT_LIST_DIR}/State_X86_64.cpp\"\n                          \"${CMAKE_CURRENT_LIST_DIR}/Options_X86_64.cpp\")\n"
  },
  {
    "path": "tools/pyqbdi/binding/X86_64/Options_X86_64.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"Enum.hpp\"\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Options(py::module_ &m) {\n\n  enum_int_flag_<Options>(m, \"Options\", \"VM options\", py::arithmetic())\n      .value(\"NO_OPT\", Options::NO_OPT, \"Default value\")\n      .value(\"OPT_DISABLE_FPR\", Options::OPT_DISABLE_FPR,\n             \"Disable all operation on FPU (SSE, AVX, SIMD). May break the \"\n             \"execution if the target use the FPU.\")\n      .value(\"OPT_DISABLE_OPTIONAL_FPR\", Options::OPT_DISABLE_OPTIONAL_FPR,\n             \"Disable context switch optimisation when the target execblock \"\n             \"doesn't used FPR\")\n      .value(\"OPT_DISABLE_MEMORYACCESS_VALUE\",\n             Options::OPT_DISABLE_MEMORYACCESS_VALUE,\n             \"Don't load memory access value\")\n      .value(\"OPT_DISABLE_ERRNO_BACKUP\", Options::OPT_DISABLE_ERRNO_BACKUP,\n             \"Don't save and restore errno\")\n      .value(\"OPT_ATT_SYNTAX\", Options::OPT_ATT_SYNTAX,\n             \"Used the AT&T syntax for instruction disassembly\")\n      .value(\"OPT_ENABLE_FS_GS\", Options::OPT_ENABLE_FS_GS,\n             \"Enable Backup/Restore of FS/GS segment. This option uses the \"\n             \"instructions (RD|WR)(FS|GS)BASE that must be supported by the \"\n             \"operating system.\")\n      .export_values()\n      .def_invert()\n      .def_repr_str();\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/X86_64/State_X86_64.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid hexify_register(std::ostringstream &out, const char *s, int size) {\n  for (int i = size - 1; i >= 0; i--) {\n    out << std::setw(2) << (static_cast<unsigned>(s[i]) & 0xff);\n  }\n}\n\nvoid init_binding_State(py::module_ &m) {\n\n  py::class_<FPControl>(m, \"FPControl\")\n      .def(py::init<>())\n      .def_property(\n          \"invalid\", [](const FPControl &t) { return t.invalid; },\n          [](FPControl &t, int v) { t.invalid = v & 1; })\n      .def_property(\n          \"denorm\", [](const FPControl &t) { return t.denorm; },\n          [](FPControl &t, int v) { t.denorm = v & 1; })\n      .def_property(\n          \"zdiv\", [](const FPControl &t) { return t.zdiv; },\n          [](FPControl &t, int v) { t.zdiv = v & 1; })\n      .def_property(\n          \"ovrfl\", [](const FPControl &t) { return t.ovrfl; },\n          [](FPControl &t, int v) { t.ovrfl = v & 1; })\n      .def_property(\n          \"undfl\", [](const FPControl &t) { return t.undfl; },\n          [](FPControl &t, int v) { t.undfl = v & 1; })\n      .def_property(\n          \"precis\", [](const FPControl &t) { return t.precis; },\n          [](FPControl &t, int v) { t.precis = v & 1; })\n      .def_property(\n          \"pc\", [](const FPControl &t) { return t.pc; },\n          [](FPControl &t, int v) { t.pc = v & 3; })\n      .def_property(\n          \"rc\", [](const FPControl &t) { return t.rc; },\n          [](FPControl &t, int v) { t.rc = v & 3; });\n\n  py::class_<FPStatus>(m, \"FPStatus\")\n      .def(py::init<>())\n      .def_property(\n          \"invalid\", [](const FPStatus &t) { return t.invalid; },\n          [](FPStatus &t, int v) { t.invalid = v & 1; })\n      .def_property(\n          \"denorm\", [](const FPStatus &t) { return t.denorm; },\n          [](FPStatus &t, int v) { t.denorm = v & 1; })\n      .def_property(\n          \"zdiv\", [](const FPStatus &t) { return t.zdiv; },\n          [](FPStatus &t, int v) { t.zdiv = v & 1; })\n      .def_property(\n          \"ovrfl\", [](const FPStatus &t) { return t.ovrfl; },\n          [](FPStatus &t, int v) { t.ovrfl = v & 1; })\n      .def_property(\n          \"undfl\", [](const FPStatus &t) { return t.undfl; },\n          [](FPStatus &t, int v) { t.undfl = v & 1; })\n      .def_property(\n          \"precis\", [](const FPStatus &t) { return t.precis; },\n          [](FPStatus &t, int v) { t.precis = v & 1; })\n      .def_property(\n          \"stkflt\", [](const FPStatus &t) { return t.stkflt; },\n          [](FPStatus &t, int v) { t.stkflt = v & 1; })\n      .def_property(\n          \"errsumm\", [](const FPStatus &t) { return t.errsumm; },\n          [](FPStatus &t, int v) { t.errsumm = v & 1; })\n      .def_property(\n          \"c0\", [](const FPStatus &t) { return t.c0; },\n          [](FPStatus &t, int v) { t.c0 = v & 1; })\n      .def_property(\n          \"c1\", [](const FPStatus &t) { return t.c1; },\n          [](FPStatus &t, int v) { t.c1 = v & 1; })\n      .def_property(\n          \"c2\", [](const FPStatus &t) { return t.c2; },\n          [](FPStatus &t, int v) { t.c2 = v & 1; })\n      .def_property(\n          \"tos\", [](const FPStatus &t) { return t.tos; },\n          [](FPStatus &t, int v) { t.tos = v & 7; })\n      .def_property(\n          \"c3\", [](const FPStatus &t) { return t.c3; },\n          [](FPStatus &t, int v) { t.c3 = v & 1; })\n      .def_property(\n          \"busy\", [](const FPStatus &t) { return t.busy; },\n          [](FPStatus &t, int v) { t.busy = v & 1; });\n\n  py::class_<MMSTReg>(m, \"MMSTReg\")\n      .def(py::init<>())\n      .def_property(\n          \"st\",\n          [](const MMSTReg &t) { return py::bytes(t.reg, sizeof(t.reg)); },\n          [](MMSTReg &t, py::bytes v) {\n            std::string(v).copy(t.reg, sizeof(t.reg), 0);\n          });\n\n  py::class_<FPRState>(m, \"FPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"fcw\", &FPRState::fcw, \"x87 FPU control word\")\n      .def_readwrite(\"rfcw\", &FPRState::rfcw, \"x87 FPU control word\")\n      .def_readwrite(\"fsw\", &FPRState::fsw, \"x87 FPU status word\")\n      .def_readwrite(\"rfsw\", &FPRState::rfsw, \"x87 FPU status word\")\n      .def_readwrite(\"ftw\", &FPRState::ftw, \"x87 FPU tag word\")\n      .def_readwrite(\"fop\", &FPRState::fop, \"x87 FPU Opcode\")\n      .def_readwrite(\"ip\", &FPRState::ip, \"x87 FPU Instruction Pointer offset\")\n      .def_readwrite(\"cs\", &FPRState::cs,\n                     \"x87 FPU Instruction Pointer Selector\")\n      .def_readwrite(\"dp\", &FPRState::dp,\n                     \"x87 FPU Instruction Operand(Data) Pointer offset\")\n      .def_readwrite(\"ds\", &FPRState::ds,\n                     \"x87 FPU Instruction Operand(Data) Pointer Selector\")\n      .def_readwrite(\"mxcsr\", &FPRState::mxcsr, \"MXCSR Register state\")\n      .def_readwrite(\"mxcsrmask\", &FPRState::mxcsrmask, \"MXCSR mask\")\n      .def_readwrite(\"stmm0\", &FPRState::stmm0, \"ST0/MM0\")\n      .def_readwrite(\"stmm1\", &FPRState::stmm1, \"ST1/MM1\")\n      .def_readwrite(\"stmm2\", &FPRState::stmm2, \"ST2/MM2\")\n      .def_readwrite(\"stmm3\", &FPRState::stmm3, \"ST3/MM3\")\n      .def_readwrite(\"stmm4\", &FPRState::stmm4, \"ST4/MM4\")\n      .def_readwrite(\"stmm5\", &FPRState::stmm5, \"ST5/MM5\")\n      .def_readwrite(\"stmm6\", &FPRState::stmm6, \"ST6/MM6\")\n      .def_readwrite(\"stmm7\", &FPRState::stmm7, \"ST7/MM7\")\n      .def_property(\n          \"xmm0\",\n          [](const FPRState &t) { return py::bytes(t.xmm0, sizeof(t.xmm0)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm0, sizeof(t.xmm0), 0);\n          },\n          \"XMM 0\")\n      .def_property(\n          \"xmm1\",\n          [](const FPRState &t) { return py::bytes(t.xmm1, sizeof(t.xmm1)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm1, sizeof(t.xmm1), 0);\n          },\n          \"XMM 1\")\n      .def_property(\n          \"xmm2\",\n          [](const FPRState &t) { return py::bytes(t.xmm2, sizeof(t.xmm2)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm2, sizeof(t.xmm2), 0);\n          },\n          \"XMM 2\")\n      .def_property(\n          \"xmm3\",\n          [](const FPRState &t) { return py::bytes(t.xmm3, sizeof(t.xmm3)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm3, sizeof(t.xmm3), 0);\n          },\n          \"XMM 3\")\n      .def_property(\n          \"xmm4\",\n          [](const FPRState &t) { return py::bytes(t.xmm4, sizeof(t.xmm4)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm4, sizeof(t.xmm4), 0);\n          },\n          \"XMM 4\")\n      .def_property(\n          \"xmm5\",\n          [](const FPRState &t) { return py::bytes(t.xmm5, sizeof(t.xmm5)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm5, sizeof(t.xmm5), 0);\n          },\n          \"XMM 5\")\n      .def_property(\n          \"xmm6\",\n          [](const FPRState &t) { return py::bytes(t.xmm6, sizeof(t.xmm6)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm6, sizeof(t.xmm6), 0);\n          },\n          \"XMM 6\")\n      .def_property(\n          \"xmm7\",\n          [](const FPRState &t) { return py::bytes(t.xmm7, sizeof(t.xmm7)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm7, sizeof(t.xmm7), 0);\n          },\n          \"XMM 7\")\n      .def_property(\n          \"xmm8\",\n          [](const FPRState &t) { return py::bytes(t.xmm8, sizeof(t.xmm8)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm8, sizeof(t.xmm8), 0);\n          },\n          \"XMM 8\")\n      .def_property(\n          \"xmm9\",\n          [](const FPRState &t) { return py::bytes(t.xmm9, sizeof(t.xmm9)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm9, sizeof(t.xmm9), 0);\n          },\n          \"XMM 9\")\n      .def_property(\n          \"xmm10\",\n          [](const FPRState &t) { return py::bytes(t.xmm10, sizeof(t.xmm10)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm10, sizeof(t.xmm10), 0);\n          },\n          \"XMM 10\")\n      .def_property(\n          \"xmm11\",\n          [](const FPRState &t) { return py::bytes(t.xmm11, sizeof(t.xmm11)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm11, sizeof(t.xmm11), 0);\n          },\n          \"XMM 11\")\n      .def_property(\n          \"xmm12\",\n          [](const FPRState &t) { return py::bytes(t.xmm12, sizeof(t.xmm12)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm12, sizeof(t.xmm12), 0);\n          },\n          \"XMM 12\")\n      .def_property(\n          \"xmm13\",\n          [](const FPRState &t) { return py::bytes(t.xmm13, sizeof(t.xmm13)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm13, sizeof(t.xmm13), 0);\n          },\n          \"XMM 13\")\n      .def_property(\n          \"xmm14\",\n          [](const FPRState &t) { return py::bytes(t.xmm14, sizeof(t.xmm14)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm14, sizeof(t.xmm14), 0);\n          },\n          \"XMM 14\")\n      .def_property(\n          \"xmm15\",\n          [](const FPRState &t) { return py::bytes(t.xmm15, sizeof(t.xmm15)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.xmm15, sizeof(t.xmm15), 0);\n          },\n          \"XMM 15\")\n      .def_property(\n          \"ymm0\",\n          [](const FPRState &t) { return py::bytes(t.ymm0, sizeof(t.ymm0)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm0, sizeof(t.ymm0), 0);\n          },\n          \"YMM0[255:128]\")\n      .def_property(\n          \"ymm1\",\n          [](const FPRState &t) { return py::bytes(t.ymm1, sizeof(t.ymm1)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm1, sizeof(t.ymm1), 0);\n          },\n          \"YMM1[255:128]\")\n      .def_property(\n          \"ymm2\",\n          [](const FPRState &t) { return py::bytes(t.ymm2, sizeof(t.ymm2)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm2, sizeof(t.ymm2), 0);\n          },\n          \"YMM2[255:128]\")\n      .def_property(\n          \"ymm3\",\n          [](const FPRState &t) { return py::bytes(t.ymm3, sizeof(t.ymm3)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm3, sizeof(t.ymm3), 0);\n          },\n          \"YMM3[255:128]\")\n      .def_property(\n          \"ymm4\",\n          [](const FPRState &t) { return py::bytes(t.ymm4, sizeof(t.ymm4)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm4, sizeof(t.ymm4), 0);\n          },\n          \"YMM4[255:128]\")\n      .def_property(\n          \"ymm5\",\n          [](const FPRState &t) { return py::bytes(t.ymm5, sizeof(t.ymm5)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm5, sizeof(t.ymm5), 0);\n          },\n          \"YMM5[255:128]\")\n      .def_property(\n          \"ymm6\",\n          [](const FPRState &t) { return py::bytes(t.ymm6, sizeof(t.ymm6)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm6, sizeof(t.ymm6), 0);\n          },\n          \"YMM6[255:128]\")\n      .def_property(\n          \"ymm7\",\n          [](const FPRState &t) { return py::bytes(t.ymm7, sizeof(t.ymm7)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm7, sizeof(t.ymm7), 0);\n          },\n          \"YMM7[255:128]\")\n      .def_property(\n          \"ymm8\",\n          [](const FPRState &t) { return py::bytes(t.ymm8, sizeof(t.ymm8)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm8, sizeof(t.ymm8), 0);\n          },\n          \"YMM8[255:128]\")\n      .def_property(\n          \"ymm9\",\n          [](const FPRState &t) { return py::bytes(t.ymm9, sizeof(t.ymm9)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm9, sizeof(t.ymm9), 0);\n          },\n          \"YMM9[255:128]\")\n      .def_property(\n          \"ymm10\",\n          [](const FPRState &t) { return py::bytes(t.ymm10, sizeof(t.ymm10)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm10, sizeof(t.ymm10), 0);\n          },\n          \"YMM10[255:128]\")\n      .def_property(\n          \"ymm11\",\n          [](const FPRState &t) { return py::bytes(t.ymm11, sizeof(t.ymm11)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm11, sizeof(t.ymm11), 0);\n          },\n          \"YMM11[255:128]\")\n      .def_property(\n          \"ymm12\",\n          [](const FPRState &t) { return py::bytes(t.ymm12, sizeof(t.ymm12)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm12, sizeof(t.ymm12), 0);\n          },\n          \"YMM12[255:128]\")\n      .def_property(\n          \"ymm13\",\n          [](const FPRState &t) { return py::bytes(t.ymm13, sizeof(t.ymm13)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm13, sizeof(t.ymm13), 0);\n          },\n          \"YMM13[255:128]\")\n      .def_property(\n          \"ymm14\",\n          [](const FPRState &t) { return py::bytes(t.ymm14, sizeof(t.ymm14)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm14, sizeof(t.ymm14), 0);\n          },\n          \"YMM14[255:128]\")\n      .def_property(\n          \"ymm15\",\n          [](const FPRState &t) { return py::bytes(t.ymm15, sizeof(t.ymm15)); },\n          [](FPRState &t, py::bytes v) {\n            std::string(v).copy(t.ymm15, sizeof(t.ymm15), 0);\n          },\n          \"YMM15[255:128]\")\n      .def(\"__str__\",\n           [](const FPRState &obj) {\n             std::ostringstream oss;\n             oss << std::hex << std::setfill('0')\n                 << \"=== FPRState begin ===\" << std::endl\n                 << \"rfcw  : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.rfcw\n                 << std::endl\n                 << \"rfsw  : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.rfsw\n                 << std::endl\n                 << \"ftw   : 0x\" << std::setw(sizeof(uint8_t) * 2)\n                 << (static_cast<unsigned>(obj.ftw) & 0xff) << std::endl\n                 << \"fop   : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.fop\n                 << std::endl\n                 << \"ip    : 0x\" << std::setw(sizeof(uint32_t) * 2) << obj.ip\n                 << std::endl\n                 << \"cs    : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.cs\n                 << std::endl\n                 << \"dp    : 0x\" << std::setw(sizeof(uint32_t) * 2) << obj.dp\n                 << std::endl\n                 << \"ds    : 0x\" << std::setw(sizeof(uint16_t) * 2) << obj.ds\n                 << std::endl\n                 << \"mxcsr : 0x\" << std::setw(sizeof(uint32_t) * 2) << obj.mxcsr\n                 << std::endl\n                 << \"mxcsrmask : 0x\" << std::setw(sizeof(uint32_t) * 2)\n                 << obj.mxcsrmask << std::endl\n                 << \"stmm0 : 0x\";\n             hexify_register(oss, obj.stmm0.reg, sizeof(obj.stmm0.reg));\n             oss << std::endl << \"stmm1 : 0x\";\n             hexify_register(oss, obj.stmm1.reg, sizeof(obj.stmm1.reg));\n             oss << std::endl << \"stmm2 : 0x\";\n             hexify_register(oss, obj.stmm2.reg, sizeof(obj.stmm2.reg));\n             oss << std::endl << \"stmm3 : 0x\";\n             hexify_register(oss, obj.stmm3.reg, sizeof(obj.stmm3.reg));\n             oss << std::endl << \"stmm4 : 0x\";\n             hexify_register(oss, obj.stmm4.reg, sizeof(obj.stmm4.reg));\n             oss << std::endl << \"stmm5 : 0x\";\n             hexify_register(oss, obj.stmm5.reg, sizeof(obj.stmm5.reg));\n             oss << std::endl << \"stmm6 : 0x\";\n             hexify_register(oss, obj.stmm6.reg, sizeof(obj.stmm6.reg));\n             oss << std::endl << \"stmm7 : 0x\";\n             hexify_register(oss, obj.stmm7.reg, sizeof(obj.stmm7.reg));\n\n             oss << std::endl << \"xmm0  : 0x\";\n             hexify_register(oss, obj.xmm0, sizeof(obj.xmm0));\n             oss << std::endl << \"xmm1  : 0x\";\n             hexify_register(oss, obj.xmm1, sizeof(obj.xmm1));\n             oss << std::endl << \"xmm2  : 0x\";\n             hexify_register(oss, obj.xmm2, sizeof(obj.xmm2));\n             oss << std::endl << \"xmm3  : 0x\";\n             hexify_register(oss, obj.xmm3, sizeof(obj.xmm3));\n             oss << std::endl << \"xmm4  : 0x\";\n             hexify_register(oss, obj.xmm4, sizeof(obj.xmm4));\n             oss << std::endl << \"xmm5  : 0x\";\n             hexify_register(oss, obj.xmm5, sizeof(obj.xmm5));\n             oss << std::endl << \"xmm6  : 0x\";\n             hexify_register(oss, obj.xmm6, sizeof(obj.xmm6));\n             oss << std::endl << \"xmm7  : 0x\";\n             hexify_register(oss, obj.xmm7, sizeof(obj.xmm7));\n             oss << std::endl << \"xmm8  : 0x\";\n             hexify_register(oss, obj.xmm8, sizeof(obj.xmm8));\n             oss << std::endl << \"xmm9  : 0x\";\n             hexify_register(oss, obj.xmm9, sizeof(obj.xmm9));\n             oss << std::endl << \"xmm10 : 0x\";\n             hexify_register(oss, obj.xmm10, sizeof(obj.xmm10));\n             oss << std::endl << \"xmm11 : 0x\";\n             hexify_register(oss, obj.xmm11, sizeof(obj.xmm11));\n             oss << std::endl << \"xmm12 : 0x\";\n             hexify_register(oss, obj.xmm12, sizeof(obj.xmm12));\n             oss << std::endl << \"xmm13 : 0x\";\n             hexify_register(oss, obj.xmm13, sizeof(obj.xmm13));\n             oss << std::endl << \"xmm14 : 0x\";\n             hexify_register(oss, obj.xmm14, sizeof(obj.xmm14));\n             oss << std::endl << \"xmm15 : 0x\";\n             hexify_register(oss, obj.xmm15, sizeof(obj.xmm15));\n\n             oss << std::endl << \"ymm0  : 0x\";\n             hexify_register(oss, obj.ymm0, sizeof(obj.ymm0));\n             hexify_register(oss, obj.xmm0, sizeof(obj.xmm0));\n             oss << std::endl << \"ymm1  : 0x\";\n             hexify_register(oss, obj.ymm1, sizeof(obj.ymm1));\n             hexify_register(oss, obj.xmm1, sizeof(obj.xmm1));\n             oss << std::endl << \"ymm2  : 0x\";\n             hexify_register(oss, obj.ymm2, sizeof(obj.ymm2));\n             hexify_register(oss, obj.xmm2, sizeof(obj.xmm2));\n             oss << std::endl << \"ymm3  : 0x\";\n             hexify_register(oss, obj.ymm3, sizeof(obj.ymm3));\n             hexify_register(oss, obj.xmm3, sizeof(obj.xmm3));\n             oss << std::endl << \"ymm4  : 0x\";\n             hexify_register(oss, obj.ymm4, sizeof(obj.ymm4));\n             hexify_register(oss, obj.xmm4, sizeof(obj.xmm4));\n             oss << std::endl << \"ymm5  : 0x\";\n             hexify_register(oss, obj.ymm5, sizeof(obj.ymm5));\n             hexify_register(oss, obj.xmm5, sizeof(obj.xmm5));\n             oss << std::endl << \"ymm6  : 0x\";\n             hexify_register(oss, obj.ymm6, sizeof(obj.ymm6));\n             hexify_register(oss, obj.xmm6, sizeof(obj.xmm6));\n             oss << std::endl << \"ymm7  : 0x\";\n             hexify_register(oss, obj.ymm7, sizeof(obj.ymm7));\n             hexify_register(oss, obj.xmm7, sizeof(obj.xmm7));\n             oss << std::endl << \"ymm8  : 0x\";\n             hexify_register(oss, obj.ymm8, sizeof(obj.ymm8));\n             hexify_register(oss, obj.xmm8, sizeof(obj.xmm8));\n             oss << std::endl << \"ymm9  : 0x\";\n             hexify_register(oss, obj.ymm9, sizeof(obj.ymm9));\n             hexify_register(oss, obj.xmm9, sizeof(obj.xmm9));\n             oss << std::endl << \"ymm10 : 0x\";\n             hexify_register(oss, obj.ymm10, sizeof(obj.ymm10));\n             hexify_register(oss, obj.xmm10, sizeof(obj.xmm10));\n             oss << std::endl << \"ymm11 : 0x\";\n             hexify_register(oss, obj.ymm11, sizeof(obj.ymm11));\n             hexify_register(oss, obj.xmm11, sizeof(obj.xmm11));\n             oss << std::endl << \"ymm12 : 0x\";\n             hexify_register(oss, obj.ymm12, sizeof(obj.ymm12));\n             hexify_register(oss, obj.xmm12, sizeof(obj.xmm12));\n             oss << std::endl << \"ymm13 : 0x\";\n             hexify_register(oss, obj.ymm13, sizeof(obj.ymm13));\n             hexify_register(oss, obj.xmm13, sizeof(obj.xmm13));\n             oss << std::endl << \"ymm14 : 0x\";\n             hexify_register(oss, obj.ymm14, sizeof(obj.ymm14));\n             hexify_register(oss, obj.xmm14, sizeof(obj.xmm14));\n             oss << std::endl << \"ymm15 : 0x\";\n             hexify_register(oss, obj.ymm15, sizeof(obj.ymm15));\n             hexify_register(oss, obj.xmm15, sizeof(obj.xmm15));\n\n             oss << std::endl << \"=== FPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\"__copy__\", [](const FPRState &state) -> FPRState { return state; })\n      .def(py::pickle(\n          [](const FPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"X86_64\", py::bytes(reinterpret_cast<const char *>(&state),\n                                    sizeof(FPRState)));\n          },\n          [](py::tuple t) -> FPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"X86_64\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"X86_64\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(FPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(FPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            FPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(FPRState));\n            return newState;\n\n          }));\n\n  m.attr(\"REG_RETURN\") = REG_RETURN;\n  m.attr(\"AVAILABLE_GPR\") = AVAILABLE_GPR;\n  m.attr(\"REG_BP\") = REG_BP;\n  m.attr(\"REG_SP\") = REG_SP;\n  m.attr(\"REG_PC\") = REG_PC;\n  m.attr(\"NUM_GPR\") = NUM_GPR;\n  m.attr(\"REG_LR\") = py::none();\n  m.attr(\"REG_FLAG\") = REG_FLAG;\n\n  py::class_<GPRState>(m, \"GPRState\")\n      .def(py::init<>())\n      .def_readwrite(\"rax\", &GPRState::rax)\n      .def_readwrite(\"rbx\", &GPRState::rbx)\n      .def_readwrite(\"rcx\", &GPRState::rcx)\n      .def_readwrite(\"rdx\", &GPRState::rdx)\n      .def_readwrite(\"rsi\", &GPRState::rsi)\n      .def_readwrite(\"rdi\", &GPRState::rdi)\n      .def_readwrite(\"r8\", &GPRState::r8)\n      .def_readwrite(\"r9\", &GPRState::r9)\n      .def_readwrite(\"r10\", &GPRState::r10)\n      .def_readwrite(\"r11\", &GPRState::r11)\n      .def_readwrite(\"r12\", &GPRState::r12)\n      .def_readwrite(\"r13\", &GPRState::r13)\n      .def_readwrite(\"r14\", &GPRState::r14)\n      .def_readwrite(\"r15\", &GPRState::r15)\n      .def_readwrite(\"rbp\", &GPRState::rbp)\n      .def_readwrite(\"rsp\", &GPRState::rsp)\n      .def_readwrite(\"rip\", &GPRState::rip)\n      .def_readwrite(\"eflags\", &GPRState::eflags)\n      .def_readwrite(\"fs\", &GPRState::fs)\n      .def_readwrite(\"gs\", &GPRState::gs)\n      // cross architecture access\n      .def_readwrite(\"REG_RETURN\", &GPRState::rax, \"shadow of rax\")\n      .def_readwrite(\"AVAILABLE_GPR\", &GPRState::rbp, \"shadow of rbp\")\n      .def_readwrite(\"REG_BP\", &GPRState::rbp, \"shadow of rbp\")\n      .def_readwrite(\"REG_SP\", &GPRState::rsp, \"shadow of rsp\")\n      .def_readwrite(\"REG_PC\", &GPRState::rip, \"shadow of rip\")\n      .def_readwrite(\"NUM_GPR\", &GPRState::eflags, \"shadow of eflags\")\n      .def_readwrite(\"REG_FLAG\", &GPRState::eflags, \"shadow of eflags\")\n      .def_property_readonly(\n          \"REG_LR\", [](const GPRState &obj) { return py::none(); },\n          \"not available on X86_64\")\n      .def(\"__str__\",\n           [](const GPRState &obj) {\n             std::ostringstream oss;\n             int r = sizeof(rword) * 2;\n             oss << std::hex << std::setfill('0')\n                 << \"=== GPRState begin ===\" << std::endl\n                 << \"rax    : 0x\" << std::setw(r) << obj.rax << std::endl\n                 << \"rbx    : 0x\" << std::setw(r) << obj.rbx << std::endl\n                 << \"rcx    : 0x\" << std::setw(r) << obj.rcx << std::endl\n                 << \"rdx    : 0x\" << std::setw(r) << obj.rdx << std::endl\n                 << \"rsi    : 0x\" << std::setw(r) << obj.rsi << std::endl\n                 << \"rdi    : 0x\" << std::setw(r) << obj.rdi << std::endl\n                 << \"r8     : 0x\" << std::setw(r) << obj.r8 << std::endl\n                 << \"r9     : 0x\" << std::setw(r) << obj.r9 << std::endl\n                 << \"r10    : 0x\" << std::setw(r) << obj.r10 << std::endl\n                 << \"r11    : 0x\" << std::setw(r) << obj.r11 << std::endl\n                 << \"r12    : 0x\" << std::setw(r) << obj.r12 << std::endl\n                 << \"r13    : 0x\" << std::setw(r) << obj.r13 << std::endl\n                 << \"r14    : 0x\" << std::setw(r) << obj.r14 << std::endl\n                 << \"r15    : 0x\" << std::setw(r) << obj.r15 << std::endl\n                 << \"rbp    : 0x\" << std::setw(r) << obj.rbp << std::endl\n                 << \"rsp    : 0x\" << std::setw(r) << obj.rsp << std::endl\n                 << \"rip    : 0x\" << std::setw(r) << obj.rip << std::endl\n                 << \"eflags : 0x\" << std::setw(r) << obj.eflags << std::endl\n                 << \"fs     : 0x\" << std::setw(r) << obj.fs << std::endl\n                 << \"gs     : 0x\" << std::setw(r) << obj.gs << std::endl\n                 << \"=== GPRState end ===\" << std::endl;\n             return oss.str();\n           })\n      .def(\n          \"__getitem__\",\n          [](const GPRState &obj, unsigned int index) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_GET(&obj, index);\n          },\n          \"Get a register like QBDI_GPR_GET\", \"index\"_a)\n      .def(\n          \"__setitem__\",\n          [](GPRState &obj, unsigned int index, rword value) {\n            if (index >= (sizeof(GPRState) / sizeof(rword))) {\n              throw pybind11::index_error(\"Out of range of GPRState\");\n            }\n            return QBDI_GPR_SET(&obj, index, value);\n          },\n          \"Set a register like QBDI_GPR_SET\", \"index\"_a, \"value\"_a)\n      .def(\"__copy__\", [](const GPRState &state) -> GPRState { return state; })\n      .def(py::pickle(\n          [](const GPRState &state) { // __getstate__\n            return py::make_tuple(\n                \"X86_64\", py::bytes(reinterpret_cast<const char *>(&state),\n                                    sizeof(GPRState)));\n          },\n          [](py::tuple t) -> GPRState { // __setstate__\n            if (t.size() != 2) {\n              throw std::runtime_error(\"Invalid state!\");\n            }\n            if (t[0].cast<std::string>() != \"X86_64\") {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected \\\"X86_64\\\", found \\\"\"\n                  << t[0].cast<std::string>() << \"\\\")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            std::string buffer = t[1].cast<std::string>();\n\n            if (buffer.size() != sizeof(GPRState)) {\n              std::ostringstream oss;\n              oss << \"Invalid state. (expected size of \" << sizeof(GPRState)\n                  << \", found size of \" << buffer.size() << \")\";\n              throw std::runtime_error(oss.str());\n            }\n\n            GPRState newState;\n            memcpy(reinterpret_cast<void *>(&newState), buffer.data(),\n                   sizeof(GPRState));\n            return newState;\n\n          }));\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/binding/callback_python.h",
    "content": "/*\n * This file is part of pyQBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef CALLBACK_PYTHON_H_\n#define CALLBACK_PYTHON_H_\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\n// Python Callback\nusing PyInstCallback = std::function<VMAction(VMInstanceRef, GPRState *,\n                                              FPRState *, py::object &)>;\nusing PyVMCallback = std::function<VMAction(\n    VMInstanceRef, const VMState *, GPRState *, FPRState *, py::object &)>;\n\nstruct InstrRuleDataCBKPython {\n  PyInstCallback cbk;\n  py::object data;\n  InstPosition position;\n  int priority;\n\n  InstrRuleDataCBKPython(PyInstCallback &cbk, py::object &data,\n                         InstPosition position, int priority = PRIORITY_DEFAULT)\n      : cbk(cbk), data(data), position(position), priority(priority) {}\n};\n\nusing PyInstrRuleCallback = std::function<std::vector<InstrRuleDataCBKPython>(\n    VMInstanceRef, const InstAnalysis *, py::object &)>;\n\n} // namespace pyQBDI\n} // namespace QBDI\n\n#endif /* CALLBACK_PYTHON_H_ */\n"
  },
  {
    "path": "tools/pyqbdi/fix_preload_lib_macos.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport subprocess\nimport sys\n\ndef read_header(libpath):\n    p = subprocess.run([\"otool\", \"-l\", libpath],\n                       check=True,\n                       capture_output=True)\n    newCommand = False\n    getRPath = False\n    getDLib = False\n    rpath = []\n    dlib = []\n    for l in p.stdout.decode(\"utf8\").split(\"\\n\"):\n        l = l.strip(' ')\n        if l.startswith(\"Load command\"):\n            newCommand = True\n            getRPath = False\n            getDLib = False\n            continue\n        if newCommand and l.startswith(\"cmd\"):\n            newCommand = False\n            getRPath = l.endswith(\"LC_RPATH\")\n            getDLib = l.endswith(\"LC_LOAD_DYLIB\")\n            continue\n        if getRPath and l.startswith(\"path\"):\n            getRPath = False\n            newPath = l.split(' ', 1)[1]\n            if newPath[-1] == \")\":\n                newPath = newPath[:newPath.rindex(' (')]\n            rpath.append(newPath)\n        if getDLib and l.startswith(\"name\"):\n            getDLib = False\n            newPath = l.split(' ', 1)[1]\n            if newPath[-1] == \")\":\n                newPath = newPath[:newPath.rindex(' (')]\n            dlib.append(newPath)\n    return rpath, dlib\n\ndef setID(libname):\n    print(\"= Set ID to {} =\".format(os.path.basename(libname)))\n    subprocess.run([\"install_name_tool\", \"-id\", os.path.basename(libname), libname],\n                   check=True)\n\ndef removeRpath(libname, path):\n    print(\"= Remove rpath {} =\".format(path))\n    subprocess.run([\"install_name_tool\", \"-delete_rpath\", path, libname],\n                   check=True)\n\ndef movLib(libname, oldlib, newlib):\n    print(\"= replace {} by {} =\".format(oldlib, newlib))\n    subprocess.run([\"install_name_tool\", \"-change\", oldlib, newlib, libname],\n                   check=True)\n\ndef resign(libname):\n    print(\"= Resign =\")\n    subprocess.run([\"codesign\", \"--force\", \"--sign\", \"-\", libname],\n                   check=True)\n\n\ndef run():\n\n    if len(sys.argv) != 2:\n        print(\"Usage : {} <lib>\".format(sys.argv[0]))\n        sys.exit(1)\n\n    libpath = sys.argv[1]\n\n    if not os.path.isfile(libpath):\n        print(\"Invalid library {}\".format(libpath))\n        sys.exit(1)\n\n    print(\"== fix library {} ==\".format(libpath))\n    rpath, dlib = read_header(libpath)\n\n    setID(libpath)\n\n    for lib in dlib:\n        if 'Python' not in lib:\n            continue\n        # set python lib to libpythonX.Y.dylib\n        # the path of libpython will be add to DYLD_LIBRARY_PATH\n        # at runtime by pyqbdipreload.py\n        targetlib = \"libpython{}.{}.dylib\".format(sys.version_info.major, sys.version_info.minor)\n        movLib(libpath, lib, targetlib)\n\n    for r in rpath:\n        removeRpath(libpath, r)\n\n    resign(libpath)\n\n\n\n\nif __name__ == \"__main__\":\n    run()\n"
  },
  {
    "path": "tools/pyqbdi/preload.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iostream>\n#include <pybind11/embed.h>\n#include <pybind11/stl.h>\n#include \"pyqbdi.hpp\"\n#include \"QBDIPreload.h\"\n\n/* pyQBDI module */\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nPYBIND11_EMBEDDED_MODULE(pyqbdi, m) {\n  m.doc() = \"python binding for QBDI\";\n  m.attr(\"__version__\") = getVersion(nullptr);\n  m.attr(\"__debug__\") = PYQBDI_DEBUG;\n  m.attr(\"__arch__\") = QBDI_ARCHITECTURE_STRING;\n  m.attr(\"__platform__\") = QBDI_PLATFORM_STRING;\n  m.attr(\"__preload__\") = true;\n\n  init_binding_Range(m);\n  init_binding_State(m);\n  init_binding_Options(m);\n  init_binding_Memory(m);\n  init_binding_InstAnalysis(m);\n  init_binding_Callback(m);\n  init_binding_VM(m);\n  init_binding_Logs(m);\n  init_binding_Errors(m);\n\n  init_utils_Float(m);\n  init_utils_Memory(m);\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n\nnamespace py = pybind11;\n\n/* Init the QBDIPreload */\nQBDIPRELOAD_INIT;\n\nint QBDI::qbdipreload_on_start(void *main) {\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint QBDI::qbdipreload_on_premain(void *gprCtx, void *fpuCtx) {\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint QBDI::qbdipreload_on_main(int argc, char **argv) {\n  const char *fileTool = std::getenv(\"PYQBDI_TOOL\");\n  std::vector<std::string> args;\n\n  if (fileTool == nullptr) {\n    std::cerr << \"QBDI::qbdipreload_on_run(): PYQBDI_TOOL not found !\"\n              << std::endl;\n    exit(1);\n  }\n\n  for (int i = 0; i < argc; i++) {\n    args.push_back(std::string(argv[i]));\n  }\n\n  py::initialize_interpreter();\n  {\n    // need to initialize sys module after initialize_interpreter()\n    py::module_ sys = py::module_::import(\"sys\");\n    sys.attr(\"argv\") = args;\n    // remove LD_PRELOAD to avoid reuse it in subprocess.popen\n    py::module_ os = py::module_::import(\"os\");\n#if defined(QBDI_PLATFORM_MACOS)\n    os.attr(\"environ\").attr(\"__delitem__\")(\"DYLD_INSERT_LIBRARIES\");\n#elif defined(QBDI_PLATFORM_LINUX) || defined(QBDI_PLATFORM_ANDROID)\n    os.attr(\"environ\").attr(\"__delitem__\")(\"LD_PRELOAD\");\n#endif\n\n    // load file before create VM object\n    try {\n      py::module_ main = py::module_::import(\"__main__\");\n      py::object scope = main.attr(\"__dict__\");\n      py::eval_file(fileTool, scope);\n    } catch (const std::exception &e) {\n      std::cerr << e.what() << std::endl;\n      exit(1);\n    }\n  }\n\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint QBDI::qbdipreload_on_run(QBDI::VMInstanceRef vm, QBDI::rword start,\n                             QBDI::rword stop) {\n  try {\n    py::module_ main = py::module_::import(\"__main__\");\n    main.attr(\"pyqbdipreload_on_run\")(vm, start, stop);\n  } catch (const std::exception &e) {\n    std::cerr << e.what() << std::endl;\n    exit(1);\n  }\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint QBDI::qbdipreload_on_exit(int status) {\n  // need to destroy atexit module before call finalize_interpreter.\n  {\n    py::module_ atexit = py::module_::import(\"atexit\");\n    atexit.attr(\"_run_exitfuncs\")();\n  }\n  py::finalize_interpreter();\n\n  return QBDIPRELOAD_NO_ERROR;\n}\n"
  },
  {
    "path": "tools/pyqbdi/pyqbdi.hpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef PYQBDI_BINDING_HPP\n#define PYQBDI_BINDING_HPP\n\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <pybind11/functional.h>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <sstream>\n#include \"QBDI.h\"\n\n#ifdef QBDI_LOG_DEBUG\n#define PYQBDI_DEBUG true\n#else\n#define PYQBDI_DEBUG false\n#endif\n\nnamespace py = pybind11;\nusing namespace pybind11::literals;\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_binding_Callback(py::module_ &m);\nvoid init_binding_Errors(py::module_ &m);\nvoid init_binding_InstAnalysis(py::module_ &m);\nvoid init_binding_Logs(py::module_ &m);\nvoid init_binding_Memory(py::module_ &m);\nvoid init_binding_Options(py::module_ &m);\nvoid init_binding_Range(py::module_ &m);\nvoid init_binding_State(py::module_ &m);\nvoid init_binding_VM(py::module_ &m);\n\nvoid init_utils_Memory(py::module_ &m);\nvoid init_utils_Float(py::module_ &m);\n\n} // namespace pyQBDI\n} // namespace QBDI\n\n#endif /* PYQBDI_BINDING_HPP */\n"
  },
  {
    "path": "tools/pyqbdi/pyqbdi_module.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\n/* pyQBDI module */\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nPYBIND11_MODULE(pyqbdi, m) {\n  m.doc() = \"python binding for QBDI\";\n  m.attr(\"__version__\") = getVersion(nullptr);\n  m.attr(\"__debug__\") = PYQBDI_DEBUG;\n  m.attr(\"__arch__\") = QBDI_ARCHITECTURE_STRING;\n  m.attr(\"__platform__\") = QBDI_PLATFORM_STRING;\n  m.attr(\"__preload__\") = false;\n\n  init_binding_Range(m);\n  init_binding_State(m);\n  init_binding_Options(m);\n  init_binding_Memory(m);\n  init_binding_InstAnalysis(m);\n  init_binding_Callback(m);\n  init_binding_VM(m);\n  init_binding_Logs(m);\n  init_binding_Errors(m);\n\n  init_utils_Float(m);\n  init_utils_Memory(m);\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/pyqbdipreload.py",
    "content": "#!/usr/bin/env python3\n\n# This file is part of pyQBDI (python binding for QBDI).\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport platform\nimport pyqbdi\nfrom ctypes import util as ctypesutil\nimport sys\nimport os\nimport argparse\nimport subprocess\n\ndef get_preloadlib():\n    if platform.system() == 'Windows':\n        import importlib\n        # ['.py', '.pyw', '.pyc', '.cp310-win_amd64.pyd', '.pyd']\n        # find correct extension on windows like .cp310-win_amd64.pyd\n        for ext in importlib.machinery.all_suffixes():\n            preloadlib = os.path.join(\n                os.path.dirname(pyqbdi.__file__),\n                f\"pyqbdipreloadlib{ext}\")\n            if os.path.isfile(preloadlib):\n                return preloadlib\n    else:\n        preloadlib = os.path.join(\n            os.path.dirname(pyqbdi.__file__),\n            os.path.basename(pyqbdi.__file__).replace(\"pyqbdi\", \"pyqbdipreloadlib\"))\n\n    if not os.path.isfile(preloadlib):\n        print(\"Cannot find pyqbdi preload library : {}\".format(preloadlib))\n        exit(1)\n    return preloadlib\n\ndef find_binary(environ, binary, canFail=False):\n    # https://docs.python.org/3.8/library/os.html#os.execve:\n    # \"execve will not use the PATH variable to locate the executable; path must contain an appropriate absolute or relative path.\"\n    # seach the binary path in PATH if needed\n    binarypath = None\n    if '/' in binary or '\\\\' in binary:\n        # absolute or relative path\n        binarypath = binary\n    else:\n        if os.path.isfile(binary):  # case for current dir on windows\n            return binary\n        if \"PATH\" in environ:\n            for p in environ[\"PATH\"].split(os.pathsep):\n                if os.path.isfile(os.path.join(p, binary)):\n                    binarypath = os.path.join(p, binary)\n                    break\n\n    if not binarypath or not os.path.isfile(binarypath):\n        if canFail:\n            return None\n        print(\"Cannot find binary {} make sure its in your PATH env\".format(binary))\n        exit(1)\n\n    return binarypath\n\ndef find_windows_inject_binary(environ):\n\n    listPreloader = [\n            os.path.join(os.path.dirname(pyqbdi.__file__), \"pyqbdiWinPreloader.exe\"),\n            \"QBDIWinPreloader.exe\"]\n\n    for binary in listPreloader:\n        binarypath = find_binary(environ, binary, True)\n        if binarypath is not None:\n            return binarypath\n\n    print(\"Cannot find Windows injector binary (within {}), make sure one of them are available\".format(listPreloader))\n    exit(1)\n\n\ndef run():\n\n    parser = argparse.ArgumentParser()\n\n    parser.add_argument(\"script\", type=str, help=\"PyQBDI script\")\n    parser.add_argument(\"target\", type=str, help=\"command to instrument\")\n    parser.add_argument(\"args\", type=str, help=\"command arguments\", nargs='*')\n\n    args = parser.parse_args()\n\n    script = args.script\n    binary = args.target\n    execargs = [args.target] + args.args\n    environ = os.environ.copy()\n\n    preloadlib = get_preloadlib()\n    binarypath = find_binary(environ, binary)\n\n    # add LD_PRELOAD or DYLD_INSERT_LIBRARIES\n    if platform.system() == 'Darwin':\n\n        environ[\"DYLD_INSERT_LIBRARIES\"] = preloadlib\n        environ[\"DYLD_LIBRARY_PATH\"] = os.path.join(sys.base_prefix, 'lib')\n        environ[\"DYLD_BIND_AT_LAUNCH\"] = \"1\"\n    elif platform.system() == 'Linux':\n        libpythonname = \"python{}.{}\".format(\n            sys.version_info.major, sys.version_info.minor)\n        libpython = ctypesutil.find_library(libpythonname)\n        if not libpython:\n            libpythonname = \"python{}.{}{}\".format(\n                sys.version_info.major, sys.version_info.minor, sys.abiflags)\n            libpython = ctypesutil.find_library(libpythonname)\n            if not libpython:\n                print(\"PyQBDI in PRELOAD mode need lib{}.so\".format(libpythonname))\n                exit(1)\n\n        for s in [libpython, preloadlib]:\n            for c in [\" \", \";\", os.pathsep]:\n                if c in s:\n                    print(f\"Error: found character '{c}' in {s}\")\n                    print(f\"PyQBDIPrelaod is based on ld.so, which didn't accept '{c}' in LD_PRELOAD.\")\n                    print(\"Please check your installation to avoid ' ' and ';' in file path\")\n                    exit(1)\n\n\n        environ[\"LD_PRELOAD\"] = os.pathsep.join([libpython, preloadlib])\n        environ[\"LD_BIND_NOW\"] = \"1\"\n    elif platform.system() == 'Windows':\n        targetbinary = binarypath\n        binarypath = find_windows_inject_binary(environ)\n        execargs = [binarypath, preloadlib, targetbinary] + execargs[1:]\n    else:\n        print(\"PyQBDI in PRELOAD mode is not supported on this platform\")\n        exit(1)\n\n    # add PYQBDI_TOOL\n    if not os.path.isfile(script):\n        print(\"Cannot find {} script\".format(script))\n        exit(1)\n    else:\n        environ[\"PYQBDI_TOOL\"] = script\n\n    if platform.system() == 'Windows':\n        proc = subprocess.run(execargs, env=environ)\n        exit(proc.returncode)\n    else:\n        os.execve(binarypath, execargs, environ)\n        print(\"Fail execve\")\n        exit(1)\n\n\nif __name__ == \"__main__\":\n    run()\n"
  },
  {
    "path": "tools/pyqbdi/utils/CMakeLists.txt",
    "content": "set(BINDING_PYTHON_SRC \"${CMAKE_CURRENT_LIST_DIR}/Memory.cpp\"\n                       \"${CMAKE_CURRENT_LIST_DIR}/Float.cpp\")\n\ntarget_sources(pyqbdi_utils INTERFACE \"${BINDING_PYTHON_SRC}\")\n"
  },
  {
    "path": "tools/pyqbdi/utils/Float.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_utils_Float(py::module_ &m) {\n\n  m.def(\n      \"encodeFloat\",\n      [](float val) {\n        union {\n          float f;\n          int32_t i;\n        } t;\n        t.f = val;\n        return t.i;\n      },\n      R\"doc(\n            Encode a float as a signed integer.\n\n            :param val: Float value\n\n            :returns: a sigend integer\n            )doc\",\n      \"val\"_a);\n\n  m.def(\n      \"encodeFloatU\",\n      [](float val) {\n        union {\n          float f;\n          uint32_t i;\n        } t;\n        t.f = val;\n        return t.i;\n      },\n      R\"doc(\n            Encode a float as an unsigned interger.\n\n            :param val: Float value\n\n            :returns: an unsigned integer\n            )doc\",\n      \"val\"_a);\n\n  m.def(\n      \"decodeFloat\",\n      [](int32_t val) {\n        union {\n          float f;\n          int32_t i;\n        } t;\n        t.i = val;\n        return t.f;\n      },\n      R\"doc(\n            Encode a sigend integer as a float.\n\n            :param val: signed integer value\n\n            :returns: a float\n            )doc\",\n      \"val\"_a);\n\n  m.def(\n      \"decodeFloatU\",\n      [](uint32_t val) {\n        union {\n          float f;\n          uint32_t i;\n        } t;\n        t.i = val;\n        return t.f;\n      },\n      R\"doc(\n            Encode an unsigend integer as a float.\n\n            :param val: unsigned integer value\n\n            :returns: a float\n            )doc\",\n      \"val\"_a);\n\n  m.def(\n      \"encodeDouble\",\n      [](double val) {\n        union {\n          double f;\n          int64_t i;\n        } t;\n        t.f = val;\n        return t.i;\n      },\n      R\"doc(\n            Encode a double as a signed integer.\n\n            :param val: Double value\n\n            :returns: a sigend integer\n            )doc\",\n      \"val\"_a);\n\n  m.def(\n      \"encodeDoubleU\",\n      [](double val) {\n        union {\n          double f;\n          uint64_t i;\n        } t;\n        t.f = val;\n        return t.i;\n      },\n      R\"doc(\n            Encode a double as an unsigned interger.\n\n            :param val: Double value\n\n            :returns: an unsigned integer\n            )doc\",\n      \"val\"_a);\n\n  m.def(\n      \"decodeDouble\",\n      [](int64_t val) {\n        union {\n          double f;\n          int64_t i;\n        } t;\n        t.i = val;\n        return t.f;\n      },\n      R\"doc(\n            Encode a sigend integer as a double.\n\n            :param val: signed integer value\n\n            :returns: a double\n            )doc\",\n      \"val\"_a);\n\n  m.def(\n      \"decodeDoubleU\",\n      [](uint64_t val) {\n        union {\n          double f;\n          uint64_t i;\n        } t;\n        t.i = val;\n        return t.f;\n      },\n      R\"doc(\n            Encode an unsigend integer as a double.\n\n            :param val: unsigned integer value\n\n            :returns: a double\n            )doc\",\n      \"val\"_a);\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/pyqbdi/utils/Memory.cpp",
    "content": "/*\n * This file is part of pyQBDI (python binding for QBDI).\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"pyqbdi.hpp\"\n\nnamespace QBDI {\nnamespace pyQBDI {\n\nvoid init_utils_Memory(py::module_ &m) {\n\n  m.def(\n      \"readMemory\",\n      [](QBDI::rword address, QBDI::rword size) {\n        return py::bytes(reinterpret_cast<const char *>(address), size);\n      },\n      R\"doc(\n            Read a content from a base address.\n\n            :param address: Base address\n            :param size: Read size\n\n            :returns: Bytes of content.\n\n            .. warning::\n                This API is hazardous as the whole process memory can be read.\n            )doc\",\n      \"address\"_a, \"size\"_a);\n\n  m.def(\n      \"readRword\",\n      [](QBDI::rword address) {\n        return *(reinterpret_cast<QBDI::rword *>(address));\n      },\n      R\"doc(\n            Read a rword to the specified address\n\n            :param address: Base address\n\n            :returns: the value as a unsigned integer\n\n            .. warning::\n                This API is hazardous as the whole process memory can be read.\n            )doc\",\n      \"address\"_a);\n\n  m.def(\n      \"writeMemory\",\n      [](QBDI::rword address, std::string bytes) {\n        memcpy(reinterpret_cast<void *>(address), bytes.c_str(), bytes.size());\n      },\n      R\"doc(\n            Write a memory content to a base address.\n\n            :param address: Base address\n            :param bytes: Memory content\n\n            .. warning::\n                This API is hazardous as the whole process memory can be written.\n            )doc\",\n      \"address\"_a, \"bytes\"_a);\n\n  m.def(\n      \"writeRword\",\n      [](QBDI::rword address, QBDI::rword value) {\n        *(reinterpret_cast<QBDI::rword *>(address)) = value;\n      },\n      R\"doc(\n            Write a rword in a base address.\n\n            :param address: Base address\n            :param value: The value to write, as a unsigned integer\n\n            .. warning::\n                This API is hazardous as the whole process memory can be written.\n            )doc\",\n      \"address\"_a, \"value\"_a);\n\n  m.def(\n      \"allocateRword\",\n      []() {\n        QBDI::rword addr =\n            reinterpret_cast<QBDI::rword>(std::malloc(sizeof(rword)));\n        if (addr == 0) {\n          throw std::bad_alloc{};\n        }\n        return addr;\n      },\n      R\"doc(\n            Allocate a raw memory space to store a rword.\n\n            :returns: Address to a memory space to store a rword\n            )doc\");\n\n  m.def(\n      \"allocateMemory\",\n      [](QBDI::rword length) {\n        QBDI::rword addr = reinterpret_cast<QBDI::rword>(std::malloc(length));\n        if (addr == 0) {\n          throw std::bad_alloc{};\n        }\n        return addr;\n      },\n      R\"doc(\n            Allocate a raw memory space of specified length.\n\n            :param length: length of the memory space to allocate\n\n            :returns: Address to the allocated memory\n            )doc\",\n      \"length\"_a);\n\n  m.def(\n      \"freeMemory\",\n      [](QBDI::rword address) { std::free(reinterpret_cast<void *>(address)); },\n      R\"doc(\n            Free a memory space allocate with allocateRword or allocateMemory.\n\n            :param address: Address of the allocated memory\n            )doc\",\n      \"address\"_a);\n}\n\n} // namespace pyQBDI\n} // namespace QBDI\n"
  },
  {
    "path": "tools/validation_runner/RunConfig.py",
    "content": "#\n# This file is part of QBDI.\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport sys\nimport yaml\nimport multiprocessing\n\nclass TestConfig:\n    def __init__(self, conf = None):\n        if conf == None:\n            return\n        if 'command' not in conf:\n            print('Invalid test ' + str(conf))\n            sys.exit(1)\n        self.command = conf['command']\n        if 'arguments' in conf:\n            self.arguments = conf['arguments']\n        else:\n            self.arguments = []\n\n    @classmethod\n    def from_dict(cls, d):\n        self = TestConfig()\n        self.command = d['command']\n        self.arguments = list(d['arguments'].lstrip(\"['\").rstrip(\"']\").split(\"', '\"))\n        return self\n\n    def command_line(self):\n        return '{} {}'.format(self.command, ' '.join(self.arguments))\n\nclass RunConfig:\n    def __init__(self, config_file, lib=None, thread=0):\n\n        with open(config_file, 'r') as f:\n            conf = yaml.safe_load(f)\n\n        # Conf validation\n        if 'tests' not in conf or len(conf['tests']) == 0:\n            print('No tests in configuration, exiting!')\n            sys.exit(1)\n        if 'validator_path' not in conf and lib is None:\n            print('No validator_path in configuration, exiting!')\n            sys.exit(1)\n        if 'database' not in conf:\n            print('No database in configuration, exiting!')\n            sys.exit(1)\n        # Tests\n        self.tests = []\n        for test in conf['tests']:\n            self.tests.append(TestConfig(test))\n        # Thread\n        if thread != 0:\n            self.thread = thread\n            assert self.thread > 0\n        elif 'threads' in conf:\n            self.thread = int(conf['threads'])\n            assert self.thread > 0\n        else:\n            self.thread = max(1, multiprocessing.cpu_count() // 2)\n        # Validator path\n        self.validator_path = lib if lib is not None else conf['validator_path']\n        # Database\n        self.database = conf['database']\n"
  },
  {
    "path": "tools/validation_runner/RunOrchestrator.py",
    "content": "#\n# This file is part of QBDI.\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nimport os\nimport time\nimport multiprocessing\nimport subprocess\n\nfrom TestResult import TestResult\nfrom RunResult import RunResult\n\ndef run_test(test, env, idx):\n    print('[{}] Validating {}'.format(idx, test.command_line()))\n    # Setup files\n    coverage_file = '.{}_coverage'.format(idx)\n    error = False\n\n    env['VALIDATOR_COVERAGE'] = coverage_file\n    # Execute\n    try:\n        process = subprocess.run([test.command] + test.arguments, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, env=env)\n        retcode = process.returncode\n        result = process.stderr.decode('utf8')\n    except OSError as e:\n        print('[{}] Failled to execute {} : {}'.format(idx, test.command_line(), e))\n        retcode = 255\n        error = True\n        result = \"\"\n\n    # Parse test results and remove test files\n    if os.path.isfile(coverage_file):\n        with open(coverage_file, 'rb') as f:\n            coverage = f.read().decode('utf8')\n        os.remove(coverage_file)\n    else:\n        coverage = \"\"\n\n    test_result = TestResult(test, retcode, result, coverage, error)\n\n    if test_result.retcode == 0 and test_result.same_output == 1:\n        print('[{}] Validated {}'.format(idx, test.command_line()))\n    else:\n        print('[{}] Failed validation {}'.format(idx, test.command_line()))\n\n    return test_result\n\nclass RunOrchestrator:\n    def __init__(self, run_cfg):\n        self.run_cfg = run_cfg\n\n    def run(self):\n        pool = multiprocessing.Pool(processes=self.run_cfg.thread)\n        tests = self.run_cfg.tests\n        async_res = []\n        # Schedule validation on multiple process\n        for idx in range(len(tests)):\n            env = dict(os.environ, LD_PRELOAD=self.run_cfg.validator_path, VALIDATOR_VERBOSITY='Detail', LD_BIND_NOW='1')\n            async_res.append(pool.apply_async(run_test, (tests[idx], env, idx)))\n        test_results = []\n        for idx in range(len(tests)):\n            test_results.append(async_res[idx].get())\n        run_result = RunResult(test_results)\n        return run_result\n"
  },
  {
    "path": "tools/validation_runner/RunResult.py",
    "content": "#\n# This file is part of QBDI.\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nimport subprocess\nfrom operator import itemgetter\nfrom TestResult import scan_for_pattern, coverage_to_log\n\nclass RunResult:\n    def __init__(self, test_results=None):\n        if test_results == None:\n            return\n        # Init run info\n        self.get_branch_commit()\n        self.test_results = test_results\n        self.coverage = {}\n        self.memaccess_unique = {}\n        self.total_instr       = 0\n        self.errors            = 0\n        self.no_impact_err     = 0\n        self.non_critical_err  = 0\n        self.critical_err      = 0\n        self.cascades          = 0\n        self.no_impact_casc    = 0\n        self.non_critical_casc = 0\n        self.critical_casc     = 0\n        self.passed_tests      = 0\n        self.memaccess_error   = 0\n        # Compute aggregated statistics\n        for t in test_results:\n            # Only count successfull tests\n            if t.retcode == 0 and t.same_output == 1:\n                self.total_instr       += t.total_instr\n                self.errors            += t.errors\n                self.no_impact_err     += t.no_impact_err\n                self.non_critical_err  += t.non_critical_err\n                self.critical_err      += t.critical_err\n                self.cascades          += t.cascades\n                self.no_impact_casc    += t.no_impact_casc\n                self.non_critical_casc += t.non_critical_casc\n                self.critical_casc     += t.critical_casc\n                self.memaccess_error   += t.memaccess_error\n                self.passed_tests   += 1\n                # Aggregate coverage\n                for instr, count in t.coverage.items():\n                    self.coverage[instr] = self.coverage.get(instr, 0) + count\n                for instr, count in t.memaccess_unique.items():\n                    self.memaccess_unique[instr] = self.memaccess_unique.get(instr, 0) + count\n        self.total_tests = len(test_results)\n        self.unique_instr = len(self.coverage)\n        self.coverage_log = coverage_to_log(self.coverage.items())\n        self.memaccess_unique_log = coverage_to_log(self.memaccess_unique.items())\n\n    @classmethod\n    def from_dict(cls, d):\n        self = RunResult()\n        self.branch = d['branch']\n        self.commit = d['commit']\n        self.total_instr = d['total_instr']\n        self.unique_instr = d['unique_instr']\n        self.total_tests = d['total_tests']\n        self.passed_tests = d['passed_tests']\n        self.errors = d['errors']\n        self.no_impact_err = d['no_impact_err']\n        self.non_critical_err = d['non_critical_err']\n        self.critical_err = d['critical_err']\n        self.cascades = d['cascades']\n        self.no_impact_casc = d['no_impact_casc']\n        self.non_critical_casc = d['non_critical_casc']\n        self.critical_casc = d['critical_casc']\n        self.memaccess_error = d['memaccess_error']\n        self.coverage_log = d['coverage_log']\n        self.memaccess_unique_log = d['memaccess_unique_log']\n        #Rebuild coverage\n        self.coverage = {}\n        self.memaccess_unique = {}\n        for line in self.coverage_log.split('\\n'):\n            if ':' in line:\n                inst, count = line.split(':')\n                self.coverage[inst] = int(count)\n        for line in self.memaccess_unique_log.split('\\n'):\n            if ':' in line:\n                inst, count = line.split(':')\n                self.memaccess_unique[inst] = int(count)\n        self.test_results = []\n        return self\n\n    def print_stats(self):\n        print('[+] Validation result for {}:{}'.format(self.branch, self.commit))\n        print('[+] Passed {}/{} validation tests'.format(self.passed_tests, self.total_tests))\n        print('[+] Executed {} total instructions'.format(self.total_instr))\n        print('[+] Executed {} unique instructions'.format(self.unique_instr))\n        print('[+] Encountered {} memoryAccess errors'.format(self.memaccess_error))\n        print('[+] Encountered {} unique memoryAccess errors'.format(len(list(self.memaccess_unique.items()))))\n        print('[+] Encountered {} total errors:'.format(self.errors))\n        print('[+]     No impact errors: {}'.format(self.no_impact_err))\n        print('[+]     Non critical errors: {}'.format(self.non_critical_err))\n        print('[+]     Critical errors: {}'.format(self.critical_err))\n        print('[+] Encountered {} total error cascades:'.format(self.cascades))\n        print('[+]     No impact cascades: {}'.format(self.no_impact_casc))\n        print('[+]     Non critical cascades: {}'.format(self.non_critical_casc))\n        print('[+]     Critical cascades: {}'.format(self.critical_casc))\n\n    def compartive_analysis(self, db):\n        prev_run = db.get_last_run(self.branch)\n        if prev_run == None:\n            prev_run = db.get_last_run('master')\n            if prev_run == None:\n                print('[+] No previous run in the DB to compare with')\n                return\n        print('[+] Comparing with validation run from {}:{}'.format(prev_run.branch, prev_run.commit))\n        regression = 0\n        for t1 in prev_run.test_results:\n            for t2 in self.test_results:\n                # Check if the command and arguments are identical\n                if t1.cfg.command == t2.cfg.command and t1.cfg.arguments == t2.cfg.arguments:\n                    if t1.retcode == 0 and t2.retcode != 0:\n                        print('[+] ERROR: Regresssion on test {}'.format(t1.cfg.command_line()))\n                        # Warn of binary hash change\n                        if t1.binary_hash != t2.binary_hash:\n                            print('[+] \\tWARNING: Binary hashes are not the same')\n                        regression += 1\n                    elif t2.errors > t1.errors:\n                        print('[+] WARNING: Increased error count on test {}'.format(t1.cfg.command_line()))\n                        if t2.no_impact_err > t1.no_impact_err:\n                            print('[+] \\tNo impact errors increased: {} -> {}'.format(t1.no_impact_err, t2.no_impact_err))\n                        if t2.non_critical_err > t1.non_critical_err:\n                            print('[+] \\tNon critical errors increased: {} -> {}'.format(t1.non_critical_err, t2.non_critical_err))\n                        # Warn of binary hash change\n                        if t1.binary_hash != t2.binary_hash:\n                            print('[+] \\tWARNING: Binary hashes are not the same')\n\n        if regression == 0:\n            print('[+] No regression')\n        else:\n            print('[+] ERROR: {} regressions encountered'.format(regression))\n        return regression\n\n\n    def get_branch_commit(self):\n        try:\n            out = subprocess.check_output(['git', 'status', '-b', '-uno', '--porcelain=2'], universal_newlines=True)\n        except Exception as e:\n            print('[!] git command error : {}'.format(e))\n            self.commit = 'UNKNOWN'\n            self.branch = 'UNKNOWN'\n        else:\n            self.commit = scan_for_pattern(out, '# branch.oid ([0-9a-fA-F]+)')[0]\n            self.branch = scan_for_pattern(out, '# branch.head (\\\\S+)')[0]\n\n    def write_to_db(self, db):\n        run_id = db.insert_run_result(self)\n        for t in self.test_results:\n            db.insert_test_result(run_id, t)\n"
  },
  {
    "path": "tools/validation_runner/SQLite.py",
    "content": "#\n# This file is part of QBDI.\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nimport time\nimport sqlite3\n\nfrom RunResult import RunResult\nfrom TestResult import TestResult\n\nclass SQLiteDBAdapter:\n    def __init__(self, db_path):\n        self.connection = sqlite3.connect(db_path)\n        self.connection.row_factory = sqlite3.Row\n        self.setup_db()\n\n    def setup_db(self):\n        cursor = self.connection.cursor()\n        cursor.execute('''CREATE TABLE IF NOT EXISTS Runs (\n                            run_id INTEGER PRIMARY KEY AUTOINCREMENT,\n                            branch TEXT,\n                            \"commit\" TEXT,\n                            timestamp INTEGER,\n                            total_tests INTEGER,\n                            passed_tests INTEGER,\n                            total_instr INTEGER,\n                            unique_instr INTEGER,\n                            errors INTEGER,\n                            no_impact_err INTEGER,\n                            non_critical_err INTEGER,\n                            critical_err INTEGER,\n                            cascades INTEGER,\n                            no_impact_casc INTEGER,\n                            non_critical_casc INTEGER,\n                            critical_casc INTEGER,\n                            memaccess_error INTEGER,\n                            coverage_log TEXT,\n                            memaccess_unique_log TEXT);''')\n        cursor.execute('''CREATE TABLE IF NOT EXISTS Tests (\n                            test_id INTEGER PRIMARY KEY AUTOINCREMENT,\n                            run_id INTEGER,\n                            command TEXT,\n                            arguments TEXT,\n                            binary_hash TEXT,\n                            retcode INTEGER,\n                            total_instr INTEGER,\n                            unique_instr INTEGER,\n                            diff_map INTEGER,\n                            errors INTEGER,\n                            no_impact_err INTEGER,\n                            non_critical_err INTEGER,\n                            critical_err INTEGER,\n                            cascades INTEGER,\n                            no_impact_casc INTEGER,\n                            non_critical_casc INTEGER,\n                            critical_casc INTEGER,\n                            memaccess_error INTEGER,\n                            memaccess_unique_error INTEGER,\n                            memaccess_log TEXT,\n                            output_len_dbg INTEGER,\n                            output_len_dbi INTEGER,\n                            same_output INTEGER,\n                            cascades_log TEXT,\n                            coverage_log TEXT,\n                            memaccess_unique_log TEXT);''')\n        cursor.execute('''CREATE INDEX IF NOT EXISTS RunIdx on Tests (run_id);''')\n        self.connection.commit()\n\n        check_exist_column = [\n            # table, column_name, type, default_value\n            (\"Runs\", \"memaccess_error\"),\n            (\"Runs\", \"memaccess_unique_log\"),\n            (\"Tests\", \"memaccess_error\"),\n            (\"Tests\", \"memaccess_unique_error\"),\n            (\"Tests\", \"memaccess_log\"),\n            (\"Tests\", \"memaccess_unique_log\"),\n            (\"Tests\", \"same_output\"),\n            (\"Tests\", \"output_len_dbg\"),\n            (\"Tests\", \"output_len_dbi\"),\n        ]\n        for table, column_name in check_exist_column:\n            cursor.execute(\"SELECT COUNT(*) AS CNTREC FROM pragma_table_info(?) WHERE name=?\", (table, column_name))\n            row = cursor.fetchone()\n            cursor.fetchall()\n            if row[0] == 0:\n                # old version of the database, just drop all and begin a new DB\n                print('[!] Old database, drop tables and begin new tables')\n                cursor.execute('DROP TABLE Tests;')\n                cursor.execute('DROP TABLE Runs;')\n                self.connection.commit()\n                self.setup_db()\n\n\n\n    def insert_run_result(self, run_result):\n        cursor = self.connection.cursor()\n        cursor.execute('''INSERT INTO Runs (branch, \"commit\", timestamp, total_tests, passed_tests,\n                          total_instr, unique_instr, errors, no_impact_err, non_critical_err,\n                          critical_err, cascades, no_impact_casc, non_critical_casc, critical_casc,\n                          memaccess_error, coverage_log, memaccess_unique_log) VALUES\n                          (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);''',\n                          (run_result.branch, run_result.commit, int(time.time()), run_result.total_tests,\n                              run_result.passed_tests, run_result.total_instr, run_result.unique_instr,\n                              run_result.errors, run_result.no_impact_err, run_result.non_critical_err,\n                              run_result.critical_err, run_result.cascades, run_result.no_impact_casc,\n                              run_result.non_critical_casc, run_result.critical_casc, run_result.memaccess_error,\n                              run_result.coverage_log,run_result.memaccess_unique_log))\n        run_id = cursor.lastrowid\n        self.connection.commit()\n        return run_id\n\n    def insert_test_result(self, run_id, test_result):\n        cursor = self.connection.cursor()\n        cursor.execute('''INSERT INTO Tests (run_id, command, arguments, binary_hash, retcode,\n                          total_instr, unique_instr, diff_map, errors, no_impact_err, non_critical_err,\n                          critical_err, cascades, no_impact_casc, non_critical_casc, critical_casc,\n                          memaccess_error, memaccess_unique_error, memaccess_log, output_len_dbg, output_len_dbi,\n                          same_output, cascades_log, coverage_log, memaccess_unique_log) VALUES\n                          (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);''',\n                          (run_id, test_result.cfg.command, str(test_result.cfg.arguments),\n                              test_result.binary_hash, test_result.retcode, test_result.total_instr,\n                              test_result.unique_instr, test_result.diff_map, test_result.errors,\n                              test_result.no_impact_err, test_result.non_critical_err, test_result.critical_err,\n                              test_result.cascades, test_result.no_impact_casc, test_result.non_critical_casc,\n                              test_result.critical_casc, test_result.memaccess_error, test_result.memaccess_unique_error,\n                              test_result.memaccess_log, test_result.output_len_dbg, test_result.output_len_dbi,\n                              test_result.same_output, test_result.cascades_log,\n                              test_result.coverage_log, test_result.memaccess_unique_log))\n        run_id = cursor.lastrowid\n        self.connection.commit()\n        return run_id\n\n    def get_last_run(self, branch):\n        cursor = self.connection.cursor()\n        # Find run result\n        cursor.execute('select * from Runs where branch=? order by timestamp desc;', (branch,))\n        row = cursor.fetchone()\n        if row == None:\n            return None\n        # Rebuild RunResult object Run row\n        run_result = RunResult.from_dict({k: row[k] for k in row.keys()})\n        # Find test results\n        for row in cursor.execute('select * from Tests where run_id=?;', (row['run_id'],)):\n            # Rebuild TestResult object Test row\n            run_result.test_results.append(TestResult.from_dict({k: row[k] for k in row.keys()}))\n        return run_result\n"
  },
  {
    "path": "tools/validation_runner/TestResult.py",
    "content": "#\n# This file is part of QBDI.\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nimport os\nimport re\nimport hashlib\nfrom operator import itemgetter\n\nfrom RunConfig import TestConfig\n\nclass Scan_Pattern_Exception(Exception):\n    pass\n\ndef scan_for_pattern(data, pattern):\n    m = re.search(pattern, data)\n    if m != None:\n        return m.groups()\n    raise Scan_Pattern_Exception(\"Not found {} in :\\n{}\".format(pattern, data))\n\ndef scan_for_multipattern(data, pattern):\n    r = {}\n    for m in re.finditer(pattern, data):\n        r[m.groups()[0]] = r.get(m.groups()[0], 0) + 1\n    return r\n\ndef coverage_to_log(coverage):\n    coverage = list(coverage)\n    coverage.sort(key=itemgetter(1), reverse=True)\n    return '\\n'.join(map(lambda x: '{}: {}'.format(x[0], x[1]), coverage))\n\nclass TestResult:\n    def __init__(self, cfg = None, retcode = None, result = None, coverage = None, error=False):\n        if cfg == None or retcode == None or result == None or coverage == None:\n            return\n        self.cfg = cfg\n        self.retcode = retcode\n        self.exec_error = error\n        self.binary_hash = self.get_binary_hash()\n        # Process coverage file, rebuilding a dictionnary from it\n        self.coverage = {}\n        self.memaccess_unique = {}\n        if not error:\n            for line in coverage.split('\\n'):\n                if ':' in line:\n                    inst, count = line.split(':')\n                    self.coverage[inst] = int(count)\n\n            try:\n                # Process result file, getting statistics\n                self.total_instr = int(scan_for_pattern(result, 'Executed (\\\\d+) total instructions')[0])\n                self.unique_instr = int(scan_for_pattern(result, 'Executed (\\\\d+) unique instructions')[0])\n                self.diff_map = int(scan_for_pattern(result, 'Encountered (\\\\d+) difference mappings')[0])\n                self.errors = int(scan_for_pattern(result, 'Encountered (\\\\d+) errors')[0])\n                self.no_impact_err = int(scan_for_pattern(result, 'No impact errors: (\\\\d+)')[0])\n                self.non_critical_err = int(scan_for_pattern(result, 'Non critical errors: (\\\\d+)')[0])\n                self.critical_err = int(scan_for_pattern(result, 'Critical errors: (\\\\d+)')[0])\n                self.cascades = int(scan_for_pattern(result, 'Encountered (\\\\d+) error cascades')[0])\n                self.no_impact_casc = int(scan_for_pattern(result, 'No impact cascades: (\\\\d+)')[0])\n                self.non_critical_casc = int(scan_for_pattern(result, 'Non critical cascades: (\\\\d+)')[0])\n                self.critical_casc = int(scan_for_pattern(result, 'Critical cascades: (\\\\d+)')[0])\n                self.memaccess_error = int(scan_for_pattern(result, 'Encountered (\\\\d+) memoryAccess errors')[0])\n                self.memaccess_unique_error = int(scan_for_pattern(result, 'Encountered (\\\\d+) memoryAccess unique errors')[0])\n                self.output_len_dbg = int(scan_for_pattern(result, 'SizeOutput: (\\\\d+) (\\\\d+)')[0])\n                self.output_len_dbi = int(scan_for_pattern(result, 'SizeOutput: (\\\\d+) (\\\\d+)')[1])\n                self.same_output = 1 if 'SameOutput: True' in result else 0\n            except Scan_Pattern_Exception as e:\n                print(\"[!] {}\".format(e))\n                error = True\n                self.retcode = 255\n            else:\n                # Storing logs\n                memAccess_start = result.find('Error MemoryAccess:')\n                cascade_start = result.find('Error cascades:')\n                self.memaccess_log = result[memAccess_start:cascade_start]\n                self.cascades_log = result[cascade_start:]\n                self.coverage_log = coverage_to_log(self.coverage.items())\n\n                self.memaccess_unique = scan_for_multipattern(self.memaccess_log, \"MemoryAccess Error \\\\(mnemonic : ([^\\\\)]+)\\\\)\")\n                self.memaccess_unique_log = coverage_to_log(self.memaccess_unique.items())\n        if error:\n            # Process result file, getting statistics\n            self.total_instr = 0\n            self.unique_instr = 0\n            self.diff_map = 0\n            self.errors = 0\n            self.no_impact_err = 0\n            self.non_critical_err = 0\n            self.critical_err = 0\n            self.cascades = 0\n            self.no_impact_casc = 0\n            self.non_critical_casc = 0\n            self.critical_casc = 0\n            self.memaccess_error = 0\n            self.memaccess_unique_error = 0\n            self.output_len_dbg = 0\n            self.output_len_dbi = 0\n            self.same_output = 0\n\n            # Storing logs\n            self.memaccess_log = \"\"\n            self.cascades_log = \"\"\n            self.coverage_log = \"\"\n            self.memaccess_unique_log = \"\"\n\n    @classmethod\n    def from_dict(cls, d):\n        self = TestResult()\n        self.binary_hash = d['binary_hash']\n        self.retcode = d['retcode']\n        self.total_instr = d['total_instr']\n        self.unique_instr = d['unique_instr']\n        self.diff_map = d['diff_map']\n        self.errors = d['errors']\n        self.no_impact_err = d['no_impact_err']\n        self.non_critical_err = d['non_critical_err']\n        self.critical_err = d['critical_err']\n        self.cascades = d['cascades']\n        self.no_impact_casc = d['no_impact_casc']\n        self.non_critical_casc = d['non_critical_casc']\n        self.critical_casc = d['critical_casc']\n        self.memaccess_error = d['memaccess_error']\n        self.memaccess_unique_error = d['memaccess_unique_error']\n        self.memaccess_log = d['memaccess_log']\n        self.cascades_log = d['cascades_log']\n        self.coverage_log = d['coverage_log']\n        self.memaccess_unique_log = d['memaccess_unique_log']\n        self.output_len_dbg = d['output_len_dbg']\n        self.output_len_dbi = d['output_len_dbi']\n        self.same_output = d['same_output']\n        #Rebuild coverage\n        self.coverage = {}\n        self.memaccess_unique = {}\n        for line in self.coverage_log.split('\\n'):\n            if ':' in line:\n                inst, count = line.split(':')\n                self.coverage[inst] = int(count)\n        for line in self.memaccess_unique_log.split('\\n'):\n            if ':' in line:\n                inst, count = line.split(':')\n                self.memaccess_unique[inst] = int(count)\n        #Rebuild config\n        self.cfg = TestConfig.from_dict(d)\n        return self\n\n    def get_binary_hash(self):\n        for path in os.environ[\"PATH\"].split(os.pathsep):\n            realpath = os.path.join(path.strip('\"'), self.cfg.command)\n            if os.path.isfile(realpath):\n                h = hashlib.sha256()\n                with open(realpath, 'rb') as f:\n                    h.update(f.read())\n                return h.hexdigest()\n        return 'UNKNOWN'\n"
  },
  {
    "path": "tools/validation_runner/ValidationRunner.py",
    "content": "#!/usr/bin/env python3\n# This file is part of QBDI.\n#\n# Copyright 2017 - 2025 Quarkslab\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nimport sys\n\nfrom SQLite import SQLiteDBAdapter\nfrom RunConfig import RunConfig, TestConfig\nfrom RunOrchestrator import RunOrchestrator\nimport argparse\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n\n    parser.add_argument(\"-l\", \"--lib\", type=str, default=None, help=\"Override preload lib location\")\n    parser.add_argument(\"-t\", \"--thread\", type=int, default=0, help=\"Number of thread\")\n    parser.add_argument(\"configFile\", type=str, default=None)\n\n    args = parser.parse_args()\n\n    run_cfg = RunConfig(args.configFile, args.lib)\n    db  = SQLiteDBAdapter(run_cfg.database)\n    orchestrator = RunOrchestrator(run_cfg)\n    run_result = orchestrator.run()\n    run_result.print_stats()\n    reg = run_result.compartive_analysis(db)\n    run_result.write_to_db(db)\n    sys.exit(reg)\n"
  },
  {
    "path": "tools/validation_runner/buildbot.cfg",
    "content": "threads: 1\nvalidator_path: /home/buildbot/buildbot_slave/qbdi-linux-X86_64/build/QBDI/build/tools/validator/libvalidator2.so\ndatabase: validation.db\ntests:\n# Filesystem\n    - command: ls\n      arguments: \n        - -alh\n        - /\n# Text files\n    - command: cat\n      arguments:\n        - testfiles/hamlet.txt\n    - command: head\n      arguments:\n        - testfiles/hamlet.txt\n    - command: tail\n      arguments:\n        - testfiles/hamlet.txt\n    - command: strings\n      arguments:\n        - testfiles/hamlet.txt\n# Image\n    - command: convert\n      arguments:\n        - -scale\n        - 77%\n        - testfiles/doge.jpg\n        - png:/dev/null\n# Binary\n    - command: xxd\n      arguments:\n        - testfiles/hamlet.txt\n    - command: file\n      arguments:\n        - testfiles/hamlet.txt\n    - command: zip\n      arguments:\n        - \"-\"\n        - testfiles/hamlet.txt\n# Crypto\n    - command: md5sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha1sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha256sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha384sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha512sum\n      arguments:\n        - testfiles/hamlet.txt\n"
  },
  {
    "path": "tools/validation_runner/coverage.cfg",
    "content": "validator_path: ../../build/tools/validator/libvalidator.so\ndatabase: test.db\ntests:\n# Filesystem\n    - command: ls\n      arguments: \n        - -alh\n        - /\n# Text files\n    - command: cat\n      arguments:\n        - testfiles/hamlet.txt\n    - command: head\n      arguments:\n        - testfiles/hamlet.txt\n    - command: tail\n      arguments:\n        - testfiles/hamlet.txt\n    - command: strings\n      arguments:\n        - testfiles/hamlet.txt\n# Image\n    - command: convert\n      arguments:\n        - -scale\n        - 77%\n        - testfiles/doge.jpg\n        - png:/dev/null\n# Binary\n    - command: base64\n      arguments:\n        - testfiles/hamlet.txt\n    - command: xxd\n      arguments:\n        - testfiles/hamlet.txt\n    - command: file\n      arguments:\n        - testfiles/hamlet.txt\n    - command: zip\n      arguments:\n        - \"-\"\n        - testfiles/hamlet.txt\n    - command: gzip\n      arguments:\n        - --to-stdout\n        - testfiles/doge.jpg\n# Crypto\n    - command: md5sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha1sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha256sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha384sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha512sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: openssl\n      arguments:\n        - dgst\n        - -blake2s256\n        - testfiles/hamlet.txt\n    - command: openssl\n      arguments:\n        - dgst\n        - -shake256\n        - testfiles/hamlet.txt\n    - command: openssl\n      arguments:\n        - enc\n        - -pbkdf2\n        - -aes-256-cbc\n        - -in\n        - testfiles/hamlet.txt\n        - -K\n        - \"0000000000000000000000000000000000000000000000000000000000000000\"\n        - -iv\n        - \"00000000000000000000000000000000\"\n    - command: openssl\n      arguments:\n        - enc\n        - -camellia-256-ecb\n        - -in\n        - testfiles/hamlet.txt\n        - -K\n        - \"0000000000000000000000000000000000000000000000000000000000000000\"\n"
  },
  {
    "path": "tools/validation_runner/example.cfg",
    "content": "threads: 2\nvalidator_path: ../../build/tools/validator/libvalidator.so\ndatabase: test.db\ntests:\n# Filesystem\n    - command: ls\n      arguments: \n        - -alh\n        - /\n# Text files\n    - command: cat\n      arguments:\n        - testfiles/hamlet.txt\n    - command: head\n      arguments:\n        - testfiles/hamlet.txt\n    - command: tail\n      arguments:\n        - testfiles/hamlet.txt\n    - command: strings\n      arguments:\n        - testfiles/hamlet.txt\n# Image\n    - command: convert\n      arguments:\n        - -scale\n        - 77%\n        - testfiles/doge.jpg\n        - png:/dev/null\n# Binary\n    - command: xxd\n      arguments:\n        - testfiles/hamlet.txt\n    - command: file\n      arguments:\n        - testfiles/hamlet.txt\n    - command: zip\n      arguments:\n        - \"-\"\n        - testfiles/hamlet.txt\n# Crypto\n    - command: md5sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha1sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha256sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha384sum\n      arguments:\n        - testfiles/hamlet.txt\n    - command: sha512sum\n      arguments:\n        - testfiles/hamlet.txt\n"
  },
  {
    "path": "tools/validation_runner/testfiles/hamlet.txt",
    "content": "HAMLET\tTo be, or not to be: that is the question:\n\tWhether 'tis nobler in the mind to suffer\n\tThe slings and arrows of outrageous fortune,\n\tOr to take arms against a sea of troubles,\n\tAnd by opposing end them? To die: to sleep;\n\tNo more; and by a sleep to say we end\n\tThe heart-ache and the thousand natural shocks\n\tThat flesh is heir to, 'tis a consummation\n\tDevoutly to be wish'd. To die, to sleep;\n\tTo sleep: perchance to dream: ay, there's the rub;\n\tFor in that sleep of death what dreams may come\n\tWhen we have shuffled off this mortal coil,\n\tMust give us pause: there's the respect\n\tThat makes calamity of so long life;\n\tFor who would bear the whips and scorns of time,\n\tThe oppressor's wrong, the proud man's contumely,\n\tThe pangs of despised love, the law's delay,\n\tThe insolence of office and the spurns\n\tThat patient merit of the unworthy takes,\n\tWhen he himself might his quietus make\n\tWith a bare bodkin? who would fardels bear,\n\tTo grunt and sweat under a weary life,\n\tBut that the dread of something after death,\n\tThe undiscover'd country from whose bourn\n\tNo traveller returns, puzzles the will\n\tAnd makes us rather bear those ills we have\n\tThan fly to others that we know not of?\n\tThus conscience does make cowards of us all;\n\tAnd thus the native hue of resolution\n\tIs sicklied o'er with the pale cast of thought,\n\tAnd enterprises of great pith and moment\n\tWith this regard their currents turn awry,\n\tAnd lose the name of action.--Soft you now!\n\tThe fair Ophelia! Nymph, in thy orisons\n\tBe all my sins remember'd.\n"
  },
  {
    "path": "tools/validation_runner/travis.cfg",
    "content": "threads: 2\nvalidator_path: build/tools/validator/libvalidator.so\ndatabase: tools/validation_runner/travis_db/travis.db\ntests:\n# Filesystem\n    - command: ls\n      arguments: \n        - -alh\n        - /\n# Text files\n    - command: cat\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: head\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: tail\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: strings\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n# Binary\n    - command: xxd\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: zip\n      arguments:\n        - \"-\"\n        - tools/validation_runner/testfiles/hamlet.txt\n# Crypto\n    - command: md5sum\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: sha1sum\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: sha256sum\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: sha384sum\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n    - command: sha512sum\n      arguments:\n        - tools/validation_runner/testfiles/hamlet.txt\n"
  },
  {
    "path": "tools/validator/AARCH64/CMakeLists.txt",
    "content": "target_sources(validator\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/validatorengine_AARCH64.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  target_sources(validator\n                 PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_AARCH64.cpp\")\nelseif(QBDI_PLATFORM_MACOS)\n  # no stub\nelse()\n  message(FATAL_ERROR \"No stub for platform ${QBDI_PLATFORM}\")\nendif()\n"
  },
  {
    "path": "tools/validator/AARCH64/linux_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"linux_AARCH64.h\"\n#include <stdlib.h>\n#include \"validator.h\"\n\n#include \"Utility/LogSys.h\"\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState) {\n  gprState->x0 = user->regs[0];\n  gprState->x1 = user->regs[1];\n  gprState->x2 = user->regs[2];\n  gprState->x3 = user->regs[3];\n  gprState->x4 = user->regs[4];\n  gprState->x5 = user->regs[5];\n  gprState->x6 = user->regs[6];\n  gprState->x7 = user->regs[7];\n  gprState->x8 = user->regs[8];\n  gprState->x9 = user->regs[9];\n  gprState->x10 = user->regs[10];\n  gprState->x11 = user->regs[11];\n  gprState->x12 = user->regs[12];\n  gprState->x13 = user->regs[13];\n  gprState->x14 = user->regs[14];\n  gprState->x15 = user->regs[15];\n  gprState->x16 = user->regs[16];\n  gprState->x17 = user->regs[17];\n  gprState->x18 = user->regs[18];\n  gprState->x19 = user->regs[19];\n  gprState->x20 = user->regs[20];\n  gprState->x21 = user->regs[21];\n  gprState->x22 = user->regs[22];\n  gprState->x23 = user->regs[23];\n  gprState->x24 = user->regs[24];\n  gprState->x25 = user->regs[25];\n  gprState->x26 = user->regs[26];\n  gprState->x27 = user->regs[27];\n  gprState->x28 = user->regs[28];\n  gprState->x29 = user->regs[29];\n  gprState->lr = user->regs[30];\n  gprState->sp = user->sp;\n  gprState->pc = user->pc;\n  gprState->nzcv = user->pstate & 0xf0000000;\n}\n\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState) {\n  fprState->v0 = user->vregs[0];\n  fprState->v1 = user->vregs[1];\n  fprState->v2 = user->vregs[2];\n  fprState->v3 = user->vregs[3];\n  fprState->v4 = user->vregs[4];\n  fprState->v5 = user->vregs[5];\n  fprState->v6 = user->vregs[6];\n  fprState->v7 = user->vregs[7];\n  fprState->v8 = user->vregs[8];\n  fprState->v9 = user->vregs[9];\n  fprState->v10 = user->vregs[10];\n  fprState->v11 = user->vregs[11];\n  fprState->v12 = user->vregs[12];\n  fprState->v13 = user->vregs[13];\n  fprState->v14 = user->vregs[14];\n  fprState->v15 = user->vregs[15];\n  fprState->v16 = user->vregs[16];\n  fprState->v17 = user->vregs[17];\n  fprState->v18 = user->vregs[18];\n  fprState->v19 = user->vregs[19];\n  fprState->v20 = user->vregs[20];\n  fprState->v21 = user->vregs[21];\n  fprState->v22 = user->vregs[22];\n  fprState->v23 = user->vregs[23];\n  fprState->v24 = user->vregs[24];\n  fprState->v25 = user->vregs[25];\n  fprState->v26 = user->vregs[26];\n  fprState->v27 = user->vregs[27];\n  fprState->v28 = user->vregs[28];\n  fprState->v29 = user->vregs[29];\n  fprState->v30 = user->vregs[30];\n  fprState->v31 = user->vregs[31];\n  fprState->fpsr = user->fpsr & 0xf800009f;\n  fprState->fpcr = user->fpcr & 0x07f79f00;\n}\n\nvoid LinuxProcess::getProcessGPR(QBDI::GPRState *gprState) {\n  GPR_STRUCT user;\n  struct iovec iovec = {&user, sizeof(user)};\n\n  if (ptrace(PTRACE_GETREGSET, this->pid, ((void *)NT_PRSTATUS), &iovec) ==\n      -1) {\n    QBDI_ERROR(\"Failed to get GPR state: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  userToGPRState(&user, gprState);\n}\n\nvoid LinuxProcess::getProcessFPR(QBDI::FPRState *fprState) {\n  FPR_STRUCT user;\n  struct iovec iovec = {&user, sizeof(user)};\n\n  if (ptrace(PTRACE_GETREGSET, this->pid, ((void *)NT_FPREGSET), &iovec) ==\n      -1) {\n    QBDI_ERROR(\"Failed to get FPR state: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  userToFPRState(&user, fprState);\n}\n"
  },
  {
    "path": "tools/validator/AARCH64/linux_AARCH64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LINUX_AARCH64_H\n#define LINUX_AARCH64_H\n\n#include <unistd.h>\n\n#include <QBDI.h>\n#include <elf.h>\n#include <sys/ptrace.h>\n#include <sys/uio.h>\n#include <sys/user.h>\n#include \"linux_process.h\"\n\n#define SIGBRK SIGTRAP\nstatic const long BRK_MASK = 0xFFFFFFFF;\nstatic const long BRK_INS =\n    0xD4207FC0; // brk 3fe;  Note than brk 3FF is used for QBDIPreload\n\ntypedef struct user_regs_struct GPR_STRUCT;\ntypedef struct user_fpsimd_struct FPR_STRUCT;\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState);\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState);\n\n#endif // LINUX_AARCH64_H\n"
  },
  {
    "path": "tools/validator/AARCH64/validatorengine_AARCH64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <cstring>\n#include <inttypes.h>\n#include \"validatorengine.h\"\n\nvoid ValidatorEngine::compareState(const QBDI::GPRState *gprStateDbg,\n                                   const QBDI::FPRState *fprStateDbg,\n                                   const QBDI::GPRState *gprStateInstr,\n                                   const QBDI::FPRState *fprStateInstr) {\n  ssize_t e;\n#define DIFF_X(Xn)                                                       \\\n  if ((e = diffGPR(Xn, gprStateDbg->x##Xn, gprStateInstr->x##Xn)) != -1) \\\n    curLogEntry->errorIDs.push_back(e);\n\n  DIFF_X(0);\n  DIFF_X(1);\n  DIFF_X(2);\n  DIFF_X(3);\n  DIFF_X(4);\n  DIFF_X(5);\n  DIFF_X(6);\n  DIFF_X(7);\n  DIFF_X(8);\n  DIFF_X(9);\n  DIFF_X(9);\n  DIFF_X(10);\n  DIFF_X(11);\n  DIFF_X(12);\n  DIFF_X(13);\n  DIFF_X(14);\n  DIFF_X(15);\n  DIFF_X(16);\n  DIFF_X(17);\n  DIFF_X(18);\n  DIFF_X(19);\n  DIFF_X(19);\n  DIFF_X(20);\n  DIFF_X(21);\n  DIFF_X(22);\n  DIFF_X(23);\n  DIFF_X(24);\n  DIFF_X(25);\n  DIFF_X(26);\n  DIFF_X(27);\n  DIFF_X(28);\n  DIFF_X(29);\n  DIFF_X(29);\n  if ((e = diffGPR(30, gprStateDbg->lr, gprStateInstr->lr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(31, gprStateDbg->sp, gprStateInstr->sp)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n\n  // get only revelant bits\n  if ((e = diffGPR(32, gprStateDbg->nzcv & 0xf0000000,\n                   gprStateInstr->nzcv & 0xf0000000)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n\n  const __uint128_t first64 = ((static_cast<__uint128_t>(1) << 64) - 1);\n// FPR\n#define DIFF_V(v)                                         \\\n  if ((e = diffSPR(#v \"[0:64]\", fprStateDbg->v & first64, \\\n                   fprStateInstr->v & first64)) != -1)    \\\n    curLogEntry->errorIDs.push_back(e);                   \\\n  if ((e = diffSPR(#v \"[64:128]\", fprStateDbg->v >> 64,   \\\n                   fprStateInstr->v >> 64)) != -1)        \\\n    curLogEntry->errorIDs.push_back(e);\n\n  DIFF_V(v0);\n  DIFF_V(v1);\n  DIFF_V(v2);\n  DIFF_V(v3);\n  DIFF_V(v4);\n  DIFF_V(v5);\n  DIFF_V(v6);\n  DIFF_V(v7);\n  DIFF_V(v8);\n  DIFF_V(v9);\n  DIFF_V(v10);\n  DIFF_V(v11);\n  DIFF_V(v12);\n  DIFF_V(v13);\n  DIFF_V(v14);\n  DIFF_V(v15);\n  DIFF_V(v16);\n  DIFF_V(v17);\n  DIFF_V(v18);\n  DIFF_V(v19);\n  DIFF_V(v20);\n  DIFF_V(v21);\n  DIFF_V(v22);\n  DIFF_V(v23);\n  DIFF_V(v24);\n  DIFF_V(v25);\n  DIFF_V(v26);\n  DIFF_V(v27);\n  DIFF_V(v28);\n  DIFF_V(v29);\n  DIFF_V(v30);\n  DIFF_V(v31);\n\n  // nzcv, fpcr and fpsr can be a single register. used mask to distinct the\n  // effect\n\n  if ((e = diff(\"fpcr\", fprStateDbg->fpcr, fprStateInstr->fpcr & 0x07f79f00)) !=\n      -1)\n    curLogEntry->errorIDs.push_back(e);\n\n  // fpsr can have the NZCV bit\n  if (((fprStateDbg->fpsr & 0xf0000000) == 0) &&\n      ((fprStateInstr->fpsr & 0xf0000000) ==\n       (gprStateInstr->nzcv & 0xf0000000))) {\n    e = diff(\"fpsr\", fprStateDbg->fpsr, fprStateInstr->fpsr & 0x0800009f);\n  } else {\n    e = diff(\"fpsr\", fprStateDbg->fpsr, fprStateInstr->fpsr & 0xf800009f);\n  }\n  if (e != -1)\n    curLogEntry->errorIDs.push_back(e);\n}\n"
  },
  {
    "path": "tools/validator/ARM/CMakeLists.txt",
    "content": "target_sources(validator\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/validatorengine_ARM.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  target_sources(validator PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_ARM.cpp\")\nelse()\n  message(FATAL_ERROR \"No stub for platform ${QBDI_PLATFORM}\")\nendif()\n"
  },
  {
    "path": "tools/validator/ARM/linux_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"linux_ARM.h\"\n#include \"validator.h\"\n\n#include <errno.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ptrace.h>\n#include <sys/user.h>\n#include <sys/wait.h>\n\n#include \"Utility/LogSys.h\"\n\nvoid LinuxProcess::setBreakpoint(void *address) {\n  this->brk_address = (void *)(((QBDI::rword)address) & (~3));\n  this->brk_value = ptrace(PTRACE_PEEKDATA, this->pid, this->brk_address, NULL);\n\n  long bytecode, mask;\n  if ((((QBDI::rword)address) & 1) == 0) {\n    bytecode = 0xE7F001F0;\n    mask = 0xFFFFFFFF;\n  } else if ((((QBDI::rword)address) & 2) == 0) {\n    // thumb 4 bytes aligned\n    bytecode = 0xDE01;\n    mask = 0xFFFF;\n  } else {\n    // thumb not 4 bytes aligned\n    bytecode = 0xDE010000;\n    mask = 0xFFFF0000;\n  }\n  if (ptrace(PTRACE_POKEDATA, this->pid, this->brk_address,\n             bytecode | (this->brk_value & (~mask))) == -1) {\n    QBDI_ERROR(\"Failed to set breakpoint: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n}\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState) {\n  gprState->r0 = user->uregs[0];\n  gprState->r1 = user->uregs[1];\n  gprState->r2 = user->uregs[2];\n  gprState->r3 = user->uregs[3];\n  gprState->r4 = user->uregs[4];\n  gprState->r5 = user->uregs[5];\n  gprState->r6 = user->uregs[6];\n  gprState->r7 = user->uregs[7];\n  gprState->r8 = user->uregs[8];\n  gprState->r9 = user->uregs[9];\n  gprState->r10 = user->uregs[10];\n  gprState->r11 = user->uregs[11];\n  gprState->r12 = user->uregs[12];\n  gprState->sp = user->uregs[13];\n  gprState->lr = user->uregs[14];\n  gprState->pc = user->uregs[15];\n  if (((user->uregs[16] >> 5) & 1) == 1) {\n    gprState->pc |= 1;\n  }\n  gprState->cpsr = user->uregs[16] & 0xf80f001f;\n}\n\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState) {\n  for (size_t i = 0; i < QBDI_NUM_FPR; i++) {\n    *((uint64_t *)&fprState->vreg.d[i]) = user->d[i];\n  }\n  fprState->fpscr = user->FPSCR;\n}\n\nvoid LinuxProcess::getProcessGPR(QBDI::GPRState *gprState) {\n  GPR_STRUCT user;\n  int ret = ptrace(PTRACE_GETREGS, this->pid, NULL, &user);\n  if (ret < 0) {\n    perror(\"Fail to get GPR\");\n  }\n  userToGPRState(&user, gprState);\n}\n\nvoid LinuxProcess::getProcessFPR(QBDI::FPRState *fprState) {\n  FPR_STRUCT user;\n  int ret = ptrace(PTRACE_GETVFPREGS, this->pid, NULL, &user);\n  if (ret < 0) {\n    perror(\"Fail to get FPR\");\n  }\n  userToFPRState(&user, fprState);\n}\n"
  },
  {
    "path": "tools/validator/ARM/linux_ARM.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef LINUX_ARM_H\n#define LINUX_ARM_H\n\n#include <unistd.h>\n\n#include <QBDI.h>\n#include <sys/ptrace.h>\n#include <sys/user.h>\n#include \"linux_process.h\"\n\n// Let's have fun with undocumented ptrace interfaces!\n// From gdb/arm-linux-nat.c\n#ifndef PTRACE_GETVFPREGS\n#define PTRACE_GETVFPREGS (__ptrace_request)27\n#define PTRACE_SETVFPREGS (__ptrace_request)28\n#endif\n\n#define SIGBRK SIGTRAP\n\n// From gdb/aarch32-linux-nat.h\n#define VFP_REGS_SIZE (32 * 8 + 4)\n\ntypedef struct user_regs GPR_STRUCT;\ntypedef struct __attribute__((__packed__)) {\n  uint64_t d[QBDI_NUM_FPR];\n  uint64_t __reserved[32 - QBDI_NUM_FPR];\n  uint32_t FPSCR;\n} FPR_STRUCT;\n\n#ifdef __cplusplus\nstatic_assert(sizeof(float) == 4, \"Bad size of float\");\nstatic_assert(sizeof(double) == 8, \"Bad size of double\");\nstatic_assert(sizeof(FPR_STRUCT) == VFP_REGS_SIZE, \"Bad size of FPR_STRUCT\");\n#endif\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState);\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState);\n\n#endif // LINUX_ARM_H\n"
  },
  {
    "path": "tools/validator/ARM/validatorengine_ARM.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <cstring>\n#include <inttypes.h>\n#include \"validatorengine.h\"\n\nvoid ValidatorEngine::compareState(const QBDI::GPRState *gprStateDbg,\n                                   const QBDI::FPRState *fprStateDbg,\n                                   const QBDI::GPRState *gprStateInstr,\n                                   const QBDI::FPRState *fprStateInstr) {\n  ssize_t e;\n\n  // GPR\n  if ((e = diffGPR(0, gprStateDbg->r0, gprStateInstr->r0)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(1, gprStateDbg->r1, gprStateInstr->r1)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(2, gprStateDbg->r2, gprStateInstr->r2)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(3, gprStateDbg->r3, gprStateInstr->r3)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(4, gprStateDbg->r4, gprStateInstr->r4)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(5, gprStateDbg->r5, gprStateInstr->r5)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(6, gprStateDbg->r6, gprStateInstr->r6)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(7, gprStateDbg->r7, gprStateInstr->r7)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(8, gprStateDbg->r8, gprStateInstr->r8)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(9, gprStateDbg->r9, gprStateInstr->r9)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(9, gprStateDbg->r9, gprStateInstr->r9)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(10, gprStateDbg->r10, gprStateInstr->r10)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(11, gprStateDbg->r11, gprStateInstr->r11)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(12, gprStateDbg->r12, gprStateInstr->r12)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(13, gprStateDbg->sp, gprStateInstr->sp)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(14, gprStateDbg->lr, gprStateInstr->lr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(16, gprStateDbg->cpsr, gprStateInstr->cpsr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n\n  // FPR\n  union {\n    double s;\n    uint32_t i[2];\n  } sreg1, sreg2;\n#define DIFF_D(name, dbg, instr)                               \\\n  sreg1.s = dbg;                                               \\\n  sreg2.s = instr;                                             \\\n  if ((e = diffSPR(name \"[0]\", sreg1.i[0], sreg2.i[0])) != -1) \\\n    curLogEntry->errorIDs.push_back(e);                        \\\n  if ((e = diffSPR(name \"[1]\", sreg1.i[1], sreg2.i[1])) != -1) \\\n    curLogEntry->errorIDs.push_back(e);\n\n  DIFF_D(\"d0\", fprStateDbg->vreg.d[0], fprStateInstr->vreg.d[0]);\n  DIFF_D(\"d1\", fprStateDbg->vreg.d[1], fprStateInstr->vreg.d[1]);\n  DIFF_D(\"d2\", fprStateDbg->vreg.d[2], fprStateInstr->vreg.d[2]);\n  DIFF_D(\"d3\", fprStateDbg->vreg.d[3], fprStateInstr->vreg.d[3]);\n  DIFF_D(\"d4\", fprStateDbg->vreg.d[4], fprStateInstr->vreg.d[4]);\n  DIFF_D(\"d5\", fprStateDbg->vreg.d[5], fprStateInstr->vreg.d[5]);\n  DIFF_D(\"d6\", fprStateDbg->vreg.d[6], fprStateInstr->vreg.d[6]);\n  DIFF_D(\"d7\", fprStateDbg->vreg.d[7], fprStateInstr->vreg.d[7]);\n  DIFF_D(\"d8\", fprStateDbg->vreg.d[8], fprStateInstr->vreg.d[8]);\n  DIFF_D(\"d9\", fprStateDbg->vreg.d[9], fprStateInstr->vreg.d[9]);\n  DIFF_D(\"d10\", fprStateDbg->vreg.d[10], fprStateInstr->vreg.d[10]);\n  DIFF_D(\"d11\", fprStateDbg->vreg.d[11], fprStateInstr->vreg.d[11]);\n  DIFF_D(\"d12\", fprStateDbg->vreg.d[12], fprStateInstr->vreg.d[12]);\n  DIFF_D(\"d13\", fprStateDbg->vreg.d[13], fprStateInstr->vreg.d[13]);\n  DIFF_D(\"d14\", fprStateDbg->vreg.d[14], fprStateInstr->vreg.d[14]);\n  DIFF_D(\"d15\", fprStateDbg->vreg.d[15], fprStateInstr->vreg.d[15]);\n#if QBDI_NUM_FPR == 32\n  DIFF_D(\"d16\", fprStateDbg->vreg.d[16], fprStateInstr->vreg.d[16]);\n  DIFF_D(\"d17\", fprStateDbg->vreg.d[17], fprStateInstr->vreg.d[17]);\n  DIFF_D(\"d18\", fprStateDbg->vreg.d[18], fprStateInstr->vreg.d[18]);\n  DIFF_D(\"d19\", fprStateDbg->vreg.d[19], fprStateInstr->vreg.d[19]);\n  DIFF_D(\"d20\", fprStateDbg->vreg.d[20], fprStateInstr->vreg.d[20]);\n  DIFF_D(\"d21\", fprStateDbg->vreg.d[21], fprStateInstr->vreg.d[21]);\n  DIFF_D(\"d22\", fprStateDbg->vreg.d[22], fprStateInstr->vreg.d[22]);\n  DIFF_D(\"d23\", fprStateDbg->vreg.d[23], fprStateInstr->vreg.d[23]);\n  DIFF_D(\"d24\", fprStateDbg->vreg.d[24], fprStateInstr->vreg.d[24]);\n  DIFF_D(\"d25\", fprStateDbg->vreg.d[25], fprStateInstr->vreg.d[25]);\n  DIFF_D(\"d26\", fprStateDbg->vreg.d[26], fprStateInstr->vreg.d[26]);\n  DIFF_D(\"d27\", fprStateDbg->vreg.d[27], fprStateInstr->vreg.d[27]);\n  DIFF_D(\"d28\", fprStateDbg->vreg.d[28], fprStateInstr->vreg.d[28]);\n  DIFF_D(\"d29\", fprStateDbg->vreg.d[29], fprStateInstr->vreg.d[29]);\n  DIFF_D(\"d30\", fprStateDbg->vreg.d[30], fprStateInstr->vreg.d[30]);\n  DIFF_D(\"d31\", fprStateDbg->vreg.d[31], fprStateInstr->vreg.d[31]);\n#endif\n\n  if ((e = diffSPR(\"fpscr\", fprStateDbg->fpscr, fprStateInstr->fpscr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n}\n"
  },
  {
    "path": "tools/validator/CMakeLists.txt",
    "content": "add_library(\n  validator SHARED\n  \"${CMAKE_CURRENT_LIST_DIR}/instrumented.cpp\"\n  \"${CMAKE_CURRENT_LIST_DIR}/master.cpp\"\n  \"${CMAKE_CURRENT_LIST_DIR}/validatorengine.cpp\"\n  \"${CMAKE_CURRENT_LIST_DIR}/pipes.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  target_sources(\n    validator PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_validator.cpp\"\n                      \"${CMAKE_CURRENT_LIST_DIR}/linux_process.cpp\")\nelseif(QBDI_PLATFORM_MACOS)\n  target_sources(\n    validator PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/darwin_validator.cpp\"\n                      \"${CMAKE_CURRENT_LIST_DIR}/darwin_process.cpp\")\nendif()\n\nif(QBDI_ARCH_X86_64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86_64/CMakeLists.txt\")\nelseif(QBDI_ARCH_X86)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/X86/CMakeLists.txt\")\nelseif(QBDI_ARCH_ARM)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/ARM/CMakeLists.txt\")\nelseif(QBDI_ARCH_AARCH64)\n  include(\"${CMAKE_CURRENT_LIST_DIR}/AARCH64/CMakeLists.txt\")\nelse()\n  message(FATAL_ERROR \"No stub for architecture ${QBDI_ARCH}\")\nendif()\n\n# Also add build directory as include path for the mach_exc.h header\ntarget_include_directories(\n  validator\n  PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\n          $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>\n          $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src>\n          $<INSTALL_INTERFACE:include>)\ntarget_link_libraries(validator PRIVATE QBDIPreload QBDI_static spdlog)\n\nset_target_properties(validator PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED\n                                                           ON)\ntarget_compile_options(validator\n                       PRIVATE $<$<COMPILE_LANGUAGE:C>:${QBDI_COMMON_C_FLAGS}>)\ntarget_compile_options(\n  validator PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${QBDI_COMMON_CXX_FLAGS}>)\n"
  },
  {
    "path": "tools/validator/X86/CMakeLists.txt",
    "content": "target_sources(validator\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/validatorengine_X86.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  target_sources(validator PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_X86.cpp\")\nelseif(QBDI_PLATFORM_MACOS)\n  # no stub\nelse()\n  message(FATAL_ERROR \"No stub for platform ${QBDI_PLATFORM}\")\nendif()\n"
  },
  {
    "path": "tools/validator/X86/linux_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"linux_X86.h\"\n#include <stdlib.h>\n#include <sys/ptrace.h>\n#include \"validator.h\"\n\n#include \"Utility/LogSys.h\"\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState) {\n  gprState->eax = user->eax;\n  gprState->ebx = user->ebx;\n  gprState->ecx = user->ecx;\n  gprState->edx = user->edx;\n  gprState->esi = user->esi;\n  gprState->edi = user->edi;\n  gprState->ebp = user->ebp;\n  gprState->esp = user->esp;\n  gprState->eip = user->eip;\n  gprState->eflags = user->eflags;\n}\n\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState) {\n  memcpy(&fprState->stmm0, &user->st_space[0], 10);\n  memcpy(&fprState->stmm1, &user->st_space[4], 10);\n  memcpy(&fprState->stmm2, &user->st_space[8], 10);\n  memcpy(&fprState->stmm3, &user->st_space[12], 10);\n  memcpy(&fprState->stmm4, &user->st_space[16], 10);\n  memcpy(&fprState->stmm5, &user->st_space[20], 10);\n  memcpy(&fprState->stmm6, &user->st_space[24], 10);\n  memcpy(&fprState->stmm7, &user->st_space[28], 10);\n  memcpy(&fprState->xmm0, &user->xmm_space[0], 16);\n  memcpy(&fprState->xmm1, &user->xmm_space[4], 16);\n  memcpy(&fprState->xmm2, &user->xmm_space[8], 16);\n  memcpy(&fprState->xmm3, &user->xmm_space[12], 16);\n  memcpy(&fprState->xmm4, &user->xmm_space[16], 16);\n  memcpy(&fprState->xmm5, &user->xmm_space[20], 16);\n  memcpy(&fprState->xmm6, &user->xmm_space[24], 16);\n  memcpy(&fprState->xmm7, &user->xmm_space[28], 16);\n  fprState->ftw = user->twd;\n  fprState->mxcsrmask = 0xffff;\n  fprState->fop = user->fop;\n  fprState->rfcw = user->cwd;\n  fprState->rfsw = user->swd;\n  fprState->mxcsr = user->mxcsr;\n}\n\nvoid LinuxProcess::getProcessGPR(QBDI::GPRState *gprState) {\n  GPR_STRUCT user;\n  if (ptrace(PTRACE_GETREGS, this->pid, NULL, &user) == -1) {\n    QBDI_ERROR(\"Failed to get GPR state: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  userToGPRState(&user, gprState);\n}\n\nvoid LinuxProcess::getProcessFPR(QBDI::FPRState *fprState) {\n  FPR_STRUCT user;\n  if (ptrace(PTRACE_GETFPXREGS, this->pid, NULL, &user) == -1) {\n    QBDI_ERROR(\"Failed to get FPR state: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  userToFPRState(&user, fprState);\n}\n"
  },
  {
    "path": "tools/validator/X86/linux_X86.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LINUX_X86_H\n#define LINUX_X86_H\n\n#include <unistd.h>\n\n#include <QBDI.h>\n#include <sys/ptrace.h>\n#include <sys/user.h>\n#include \"linux_process.h\"\n\n#define SIGBRK SIGTRAP\nstatic const long BRK_MASK = 0xFF;\nstatic const long BRK_INS = 0xCC;\n\ntypedef struct user_regs_struct GPR_STRUCT;\ntypedef struct user_fpxregs_struct FPR_STRUCT;\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState);\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState);\n\nstatic inline void fix_GPR_STRUCT(GPR_STRUCT *user) { user->eip -= 1; }\n\n#endif // LINUX_X86_H\n"
  },
  {
    "path": "tools/validator/X86/validatorengine_X86.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <cstring>\n#include <inttypes.h>\n#include \"validatorengine.h\"\n\nvoid ValidatorEngine::compareState(const QBDI::GPRState *gprStateDbg,\n                                   const QBDI::FPRState *fprStateDbg,\n                                   const QBDI::GPRState *gprStateInstr,\n                                   const QBDI::FPRState *fprStateInstr) {\n  ssize_t e;\n\n  // GPR\n  if ((e = diffGPR(0, gprStateDbg->eax, gprStateInstr->eax)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(1, gprStateDbg->ebx, gprStateInstr->ebx)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(2, gprStateDbg->ecx, gprStateInstr->ecx)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(3, gprStateDbg->edx, gprStateInstr->edx)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(4, gprStateDbg->esi, gprStateInstr->esi)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(5, gprStateDbg->edi, gprStateInstr->edi)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(6, gprStateDbg->ebp, gprStateInstr->ebp)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(7, gprStateDbg->esp, gprStateInstr->esp)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n\n  // FPR\n  union {\n    QBDI::MMSTReg st;\n    struct {\n      uint32_t m0;\n      uint32_t m1;\n      uint16_t e;\n      uint16_t reserved;\n    };\n  } streg1, streg2;\n#define DIFF_ST(name, dbg, instr)                                  \\\n  streg1.st = dbg;                                                 \\\n  streg2.st = instr;                                               \\\n  if ((e = diffSPR(name \".m[0:32]\", streg1.m0, streg2.m0)) != -1)  \\\n    curLogEntry->errorIDs.push_back(e);                            \\\n  if ((e = diffSPR(name \".m[32:64]\", streg1.m1, streg2.m1)) != -1) \\\n    curLogEntry->errorIDs.push_back(e);                            \\\n  if ((e = diffSPR(name \".e\", streg1.e, streg2.e)) != -1)          \\\n    curLogEntry->errorIDs.push_back(e);\n\n  DIFF_ST(\"st0\", fprStateDbg->stmm0, fprStateInstr->stmm0);\n  DIFF_ST(\"st1\", fprStateDbg->stmm1, fprStateInstr->stmm1);\n  DIFF_ST(\"st2\", fprStateDbg->stmm2, fprStateInstr->stmm2);\n  DIFF_ST(\"st3\", fprStateDbg->stmm3, fprStateInstr->stmm3);\n  DIFF_ST(\"st4\", fprStateDbg->stmm4, fprStateInstr->stmm4);\n  DIFF_ST(\"st5\", fprStateDbg->stmm5, fprStateInstr->stmm5);\n  DIFF_ST(\"st6\", fprStateDbg->stmm6, fprStateInstr->stmm6);\n  DIFF_ST(\"st7\", fprStateDbg->stmm7, fprStateInstr->stmm7);\n\n  union {\n    char xmm[16];\n    struct {\n      uint32_t m0;\n      uint32_t m1;\n      uint32_t m2;\n      uint32_t m3;\n    };\n  } xmmreg1, xmmreg2;\n#define DIFF_XMM(name, dbg, instr)                                  \\\n  memcpy(xmmreg1.xmm, dbg, 16);                                     \\\n  memcpy(xmmreg2.xmm, instr, 16);                                   \\\n  if ((e = diffSPR(name \"[0:32]\", xmmreg1.m0, xmmreg2.m0)) != -1)   \\\n    curLogEntry->errorIDs.push_back(e);                             \\\n  if ((e = diffSPR(name \"[32:64]\", xmmreg1.m1, xmmreg2.m1)) != -1)  \\\n    curLogEntry->errorIDs.push_back(e);                             \\\n  if ((e = diffSPR(name \"[64:96]\", xmmreg1.m2, xmmreg2.m2)) != -1)  \\\n    curLogEntry->errorIDs.push_back(e);                             \\\n  if ((e = diffSPR(name \"[96:128]\", xmmreg1.m3, xmmreg2.m3)) != -1) \\\n    curLogEntry->errorIDs.push_back(e);\n\n  DIFF_XMM(\"xmm0\", fprStateDbg->xmm0, fprStateInstr->xmm0);\n  DIFF_XMM(\"xmm1\", fprStateDbg->xmm1, fprStateInstr->xmm1);\n  DIFF_XMM(\"xmm2\", fprStateDbg->xmm2, fprStateInstr->xmm2);\n  DIFF_XMM(\"xmm3\", fprStateDbg->xmm3, fprStateInstr->xmm3);\n  DIFF_XMM(\"xmm4\", fprStateDbg->xmm4, fprStateInstr->xmm4);\n  DIFF_XMM(\"xmm5\", fprStateDbg->xmm5, fprStateInstr->xmm5);\n  DIFF_XMM(\"xmm6\", fprStateDbg->xmm6, fprStateInstr->xmm6);\n  DIFF_XMM(\"xmm7\", fprStateDbg->xmm7, fprStateInstr->xmm7);\n#if 0\n    DIFF_XMM(\"ymm0\", fprStateDbg->ymm0, fprStateInstr->ymm0);\n    DIFF_XMM(\"ymm1\", fprStateDbg->ymm1, fprStateInstr->ymm1);\n    DIFF_XMM(\"ymm2\", fprStateDbg->ymm2, fprStateInstr->ymm2);\n    DIFF_XMM(\"ymm3\", fprStateDbg->ymm3, fprStateInstr->ymm3);\n    DIFF_XMM(\"ymm4\", fprStateDbg->ymm4, fprStateInstr->ymm4);\n    DIFF_XMM(\"ymm5\", fprStateDbg->ymm5, fprStateInstr->ymm5);\n    DIFF_XMM(\"ymm6\", fprStateDbg->ymm6, fprStateInstr->ymm6);\n    DIFF_XMM(\"ymm7\", fprStateDbg->ymm7, fprStateInstr->ymm7);\n#endif\n\n  if ((e = diff(\"fcw\", fprStateDbg->rfcw, fprStateInstr->rfcw)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"fsw\", fprStateDbg->rfsw, fprStateInstr->rfsw)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"ftw\", fprStateDbg->ftw, fprStateInstr->ftw)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"fop\", fprStateDbg->fop, fprStateInstr->fop)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"mxcsr\", fprStateDbg->mxcsr, fprStateInstr->mxcsr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"mxcsrmask\", fprStateDbg->mxcsrmask,\n                fprStateInstr->mxcsrmask)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n\n  // Clear the auxilliary carry flag which generates noisy output\n  QBDI::rword eflagsDbg = (gprStateDbg->eflags) & (gprStateDbg->eflags ^ 0x4);\n  QBDI::rword eflagsInstr =\n      (gprStateInstr->eflags) & (gprStateInstr->eflags ^ 0x4);\n  if ((e = diffGPR(17, eflagsDbg, eflagsInstr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n}\n"
  },
  {
    "path": "tools/validator/X86_64/CMakeLists.txt",
    "content": "target_sources(validator\n               PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/validatorengine_X86_64.cpp\")\n\nif(QBDI_PLATFORM_LINUX OR QBDI_PLATFORM_ANDROID)\n  target_sources(validator PRIVATE \"${CMAKE_CURRENT_LIST_DIR}/linux_X86_64.cpp\")\nelseif(QBDI_PLATFORM_MACOS)\n  # no stub\nelse()\n  message(FATAL_ERROR \"No stub for platform ${QBDI_PLATFORM}\")\nendif()\n"
  },
  {
    "path": "tools/validator/X86_64/linux_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"linux_X86_64.h\"\n#include <stdlib.h>\n#include <sys/ptrace.h>\n#include \"validator.h\"\n\n#include \"Utility/LogSys.h\"\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState) {\n  gprState->rax = user->rax;\n  gprState->rbx = user->rbx;\n  gprState->rcx = user->rcx;\n  gprState->rdx = user->rdx;\n  gprState->rsi = user->rsi;\n  gprState->rdi = user->rdi;\n  gprState->rbp = user->rbp;\n  gprState->rsp = user->rsp;\n  gprState->r8 = user->r8;\n  gprState->r9 = user->r9;\n  gprState->r10 = user->r10;\n  gprState->r11 = user->r11;\n  gprState->r12 = user->r12;\n  gprState->r13 = user->r13;\n  gprState->r14 = user->r14;\n  gprState->r15 = user->r15;\n  gprState->rip = user->rip;\n  gprState->eflags = user->eflags;\n}\n\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState) {\n  memcpy(&fprState->stmm0, &user->st_space[0], 10);\n  memcpy(&fprState->stmm1, &user->st_space[4], 10);\n  memcpy(&fprState->stmm2, &user->st_space[8], 10);\n  memcpy(&fprState->stmm3, &user->st_space[12], 10);\n  memcpy(&fprState->stmm4, &user->st_space[16], 10);\n  memcpy(&fprState->stmm5, &user->st_space[20], 10);\n  memcpy(&fprState->stmm6, &user->st_space[24], 10);\n  memcpy(&fprState->stmm7, &user->st_space[28], 10);\n  memcpy(&fprState->xmm0, &user->xmm_space[0], 16);\n  memcpy(&fprState->xmm1, &user->xmm_space[4], 16);\n  memcpy(&fprState->xmm2, &user->xmm_space[8], 16);\n  memcpy(&fprState->xmm3, &user->xmm_space[12], 16);\n  memcpy(&fprState->xmm4, &user->xmm_space[16], 16);\n  memcpy(&fprState->xmm5, &user->xmm_space[20], 16);\n  memcpy(&fprState->xmm6, &user->xmm_space[24], 16);\n  memcpy(&fprState->xmm7, &user->xmm_space[28], 16);\n  memcpy(&fprState->xmm8, &user->xmm_space[32], 16);\n  memcpy(&fprState->xmm9, &user->xmm_space[36], 16);\n  memcpy(&fprState->xmm10, &user->xmm_space[40], 16);\n  memcpy(&fprState->xmm11, &user->xmm_space[44], 16);\n  memcpy(&fprState->xmm12, &user->xmm_space[48], 16);\n  memcpy(&fprState->xmm13, &user->xmm_space[52], 16);\n  memcpy(&fprState->xmm14, &user->xmm_space[56], 16);\n  memcpy(&fprState->xmm15, &user->xmm_space[60], 16);\n  fprState->ftw = user->ftw;\n  fprState->mxcsrmask = user->mxcr_mask;\n  fprState->fop = user->fop;\n  fprState->rfcw = user->cwd;\n  fprState->rfsw = user->swd;\n  fprState->mxcsr = user->mxcsr;\n}\n\nvoid LinuxProcess::getProcessGPR(QBDI::GPRState *gprState) {\n  GPR_STRUCT user;\n  if (ptrace(PTRACE_GETREGS, this->pid, NULL, &user) == -1) {\n    QBDI_ERROR(\"Failed to get GPR state: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  userToGPRState(&user, gprState);\n}\n\nvoid LinuxProcess::getProcessFPR(QBDI::FPRState *fprState) {\n  FPR_STRUCT user;\n  if (ptrace(PTRACE_GETFPREGS, this->pid, NULL, &user) == -1) {\n    QBDI_ERROR(\"Failed to get FPR state: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  userToFPRState(&user, fprState);\n}\n"
  },
  {
    "path": "tools/validator/X86_64/linux_X86_64.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LINUX_X86_64_H\n#define LINUX_X86_64_H\n\n#include <unistd.h>\n\n#include <QBDI.h>\n#include <sys/ptrace.h>\n#include <sys/user.h>\n#include \"linux_process.h\"\n\n#define SIGBRK SIGTRAP\nstatic const long BRK_MASK = 0xFF;\nstatic const long BRK_INS = 0xCC;\n\ntypedef struct user_regs_struct GPR_STRUCT;\ntypedef struct user_fpregs_struct FPR_STRUCT;\n\nvoid userToGPRState(const GPR_STRUCT *user, QBDI::GPRState *gprState);\nvoid userToFPRState(const FPR_STRUCT *user, QBDI::FPRState *fprState);\n\nstatic inline void fix_GPR_STRUCT(GPR_STRUCT *user) { user->rip -= 1; }\n\n#endif // LINUX_X86_64_H\n"
  },
  {
    "path": "tools/validator/X86_64/validatorengine_X86_64.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <algorithm>\n#include <cstring>\n#include <inttypes.h>\n#include \"validatorengine.h\"\n\nvoid ValidatorEngine::compareState(const QBDI::GPRState *gprStateDbg,\n                                   const QBDI::FPRState *fprStateDbg,\n                                   const QBDI::GPRState *gprStateInstr,\n                                   const QBDI::FPRState *fprStateInstr) {\n  ssize_t e;\n\n  // GPR\n  if ((e = diffGPR(0, gprStateDbg->rax, gprStateInstr->rax)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(1, gprStateDbg->rbx, gprStateInstr->rbx)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(2, gprStateDbg->rcx, gprStateInstr->rcx)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(3, gprStateDbg->rdx, gprStateInstr->rdx)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(4, gprStateDbg->rsi, gprStateInstr->rsi)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(5, gprStateDbg->rdi, gprStateInstr->rdi)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(6, gprStateDbg->r8, gprStateInstr->r8)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(7, gprStateDbg->r9, gprStateInstr->r9)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(8, gprStateDbg->r10, gprStateInstr->r10)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(9, gprStateDbg->r11, gprStateInstr->r11)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(10, gprStateDbg->r12, gprStateInstr->r12)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(11, gprStateDbg->r13, gprStateInstr->r13)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(12, gprStateDbg->r14, gprStateInstr->r14)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(13, gprStateDbg->r15, gprStateInstr->r15)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(14, gprStateDbg->rbp, gprStateInstr->rbp)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diffGPR(15, gprStateDbg->rsp, gprStateInstr->rsp)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n\n  // FPR\n  union {\n    QBDI::MMSTReg st;\n    struct {\n      uint32_t m0;\n      uint32_t m1;\n      uint16_t e;\n      uint16_t reserved;\n    };\n  } streg1, streg2;\n#define DIFF_ST(name, dbg, instr)                                  \\\n  streg1.st = dbg;                                                 \\\n  streg2.st = instr;                                               \\\n  if ((e = diffSPR(name \".m[0:32]\", streg1.m0, streg2.m0)) != -1)  \\\n    curLogEntry->errorIDs.push_back(e);                            \\\n  if ((e = diffSPR(name \".m[32:64]\", streg1.m1, streg2.m1)) != -1) \\\n    curLogEntry->errorIDs.push_back(e);                            \\\n  if ((e = diffSPR(name \".e\", streg1.e, streg2.e)) != -1)          \\\n    curLogEntry->errorIDs.push_back(e);\n\n  DIFF_ST(\"st0\", fprStateDbg->stmm0, fprStateInstr->stmm0);\n  DIFF_ST(\"st1\", fprStateDbg->stmm1, fprStateInstr->stmm1);\n  DIFF_ST(\"st2\", fprStateDbg->stmm2, fprStateInstr->stmm2);\n  DIFF_ST(\"st3\", fprStateDbg->stmm3, fprStateInstr->stmm3);\n  DIFF_ST(\"st4\", fprStateDbg->stmm4, fprStateInstr->stmm4);\n  DIFF_ST(\"st5\", fprStateDbg->stmm5, fprStateInstr->stmm5);\n  DIFF_ST(\"st6\", fprStateDbg->stmm6, fprStateInstr->stmm6);\n  DIFF_ST(\"st7\", fprStateDbg->stmm7, fprStateInstr->stmm7);\n\n  union {\n    char xmm[16];\n    struct {\n      uint32_t m0;\n      uint32_t m1;\n      uint32_t m2;\n      uint32_t m3;\n    };\n  } xmmreg1, xmmreg2;\n#define DIFF_XMM(name, dbg, instr)                                  \\\n  memcpy(xmmreg1.xmm, dbg, 16);                                     \\\n  memcpy(xmmreg2.xmm, instr, 16);                                   \\\n  if ((e = diffSPR(name \"[0:32]\", xmmreg1.m0, xmmreg2.m0)) != -1)   \\\n    curLogEntry->errorIDs.push_back(e);                             \\\n  if ((e = diffSPR(name \"[32:64]\", xmmreg1.m1, xmmreg2.m1)) != -1)  \\\n    curLogEntry->errorIDs.push_back(e);                             \\\n  if ((e = diffSPR(name \"[64:96]\", xmmreg1.m2, xmmreg2.m2)) != -1)  \\\n    curLogEntry->errorIDs.push_back(e);                             \\\n  if ((e = diffSPR(name \"[96:128]\", xmmreg1.m3, xmmreg2.m3)) != -1) \\\n    curLogEntry->errorIDs.push_back(e);\n\n  DIFF_XMM(\"xmm0\", fprStateDbg->xmm0, fprStateInstr->xmm0);\n  DIFF_XMM(\"xmm1\", fprStateDbg->xmm1, fprStateInstr->xmm1);\n  DIFF_XMM(\"xmm2\", fprStateDbg->xmm2, fprStateInstr->xmm2);\n  DIFF_XMM(\"xmm3\", fprStateDbg->xmm3, fprStateInstr->xmm3);\n  DIFF_XMM(\"xmm4\", fprStateDbg->xmm4, fprStateInstr->xmm4);\n  DIFF_XMM(\"xmm5\", fprStateDbg->xmm5, fprStateInstr->xmm5);\n  DIFF_XMM(\"xmm6\", fprStateDbg->xmm6, fprStateInstr->xmm6);\n  DIFF_XMM(\"xmm7\", fprStateDbg->xmm7, fprStateInstr->xmm7);\n  DIFF_XMM(\"xmm8\", fprStateDbg->xmm8, fprStateInstr->xmm8);\n  DIFF_XMM(\"xmm9\", fprStateDbg->xmm9, fprStateInstr->xmm9);\n  DIFF_XMM(\"xmm10\", fprStateDbg->xmm10, fprStateInstr->xmm10);\n  DIFF_XMM(\"xmm11\", fprStateDbg->xmm11, fprStateInstr->xmm11);\n  DIFF_XMM(\"xmm12\", fprStateDbg->xmm12, fprStateInstr->xmm12);\n  DIFF_XMM(\"xmm13\", fprStateDbg->xmm13, fprStateInstr->xmm13);\n  DIFF_XMM(\"xmm14\", fprStateDbg->xmm14, fprStateInstr->xmm14);\n  DIFF_XMM(\"xmm15\", fprStateDbg->xmm15, fprStateInstr->xmm15);\n// FIXME\n#if 0\n    DIFF_XMM(\"ymm0\", fprStateDbg->ymm0, fprStateInstr->ymm0);\n    DIFF_XMM(\"ymm1\", fprStateDbg->ymm1, fprStateInstr->ymm1);\n    DIFF_XMM(\"ymm2\", fprStateDbg->ymm2, fprStateInstr->ymm2);\n    DIFF_XMM(\"ymm3\", fprStateDbg->ymm3, fprStateInstr->ymm3);\n    DIFF_XMM(\"ymm4\", fprStateDbg->ymm4, fprStateInstr->ymm4);\n    DIFF_XMM(\"ymm5\", fprStateDbg->ymm5, fprStateInstr->ymm5);\n    DIFF_XMM(\"ymm6\", fprStateDbg->ymm6, fprStateInstr->ymm6);\n    DIFF_XMM(\"ymm7\", fprStateDbg->ymm7, fprStateInstr->ymm7);\n    DIFF_XMM(\"ymm8\", fprStateDbg->ymm8, fprStateInstr->ymm8);\n    DIFF_XMM(\"ymm9\", fprStateDbg->ymm9, fprStateInstr->ymm9);\n    DIFF_XMM(\"ymm10\", fprStateDbg->ymm10, fprStateInstr->ymm10);\n    DIFF_XMM(\"ymm11\", fprStateDbg->ymm11, fprStateInstr->ymm11);\n    DIFF_XMM(\"ymm12\", fprStateDbg->ymm12, fprStateInstr->ymm12);\n    DIFF_XMM(\"ymm13\", fprStateDbg->ymm13, fprStateInstr->ymm13);\n    DIFF_XMM(\"ymm14\", fprStateDbg->ymm14, fprStateInstr->ymm14);\n    DIFF_XMM(\"ymm15\", fprStateDbg->ymm15, fprStateInstr->ymm15);\n#endif\n\n  if ((e = diff(\"fcw\", fprStateDbg->rfcw, fprStateInstr->rfcw)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"fsw\", fprStateDbg->rfsw, fprStateInstr->rfsw)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"ftw\", fprStateDbg->ftw, fprStateInstr->ftw)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"fop\", fprStateDbg->fop, fprStateInstr->fop)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"mxcsr\", fprStateDbg->mxcsr, fprStateInstr->mxcsr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  if ((e = diff(\"mxcsrmask\", fprStateDbg->mxcsrmask,\n                fprStateInstr->mxcsrmask)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n  // Clear the auxilliary carry flag which generates noisy output\n  QBDI::rword eflagsDbg = (gprStateDbg->eflags) & (gprStateDbg->eflags ^ 0x4);\n  QBDI::rword eflagsInstr =\n      (gprStateInstr->eflags) & (gprStateInstr->eflags ^ 0x4);\n  if ((e = diffGPR(17, eflagsDbg, eflagsInstr)) != -1)\n    curLogEntry->errorIDs.push_back(e);\n}\n"
  },
  {
    "path": "tools/validator/darwin_process.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"darwin_process.h\"\n#include \"validator.h\"\n#include \"QBDIPreload.h\"\n\n#include <mach/mach_error.h>\n#include <mach/mach_init.h>\n#include <mach/mach_port.h>\n#include <mach/mach_traps.h>\n#include <mach/mach_vm.h>\n#include <mach/thread_act.h>\n#include <mach/vm_prot.h>\n#include <pthread.h>\n#include <stdlib.h>\n#include <sys/event.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"Utility/LogSys.h\"\n\npthread_mutex_t STATUS_LOCK = PTHREAD_MUTEX_INITIALIZER;\npthread_cond_t STATUS_COND = PTHREAD_COND_INITIALIZER;\n\nenum Status { Running, Stopped, Crashed, Exited } STATUS;\n\nkern_return_t onBreakpoint(mach_port_t exception_port, mach_port_t thread,\n                           mach_port_t task, exception_type_t exception,\n                           mach_exception_data_t code,\n                           mach_msg_type_number_t codeCnt) {\n  kern_return_t kr;\n  mach_msg_type_number_t count;\n  THREAD_STATE threadState;\n\n  pthread_mutex_lock(&STATUS_LOCK);\n\n  if (exception == EXC_BREAKPOINT) {\n    // Getting thread state\n    count = THREAD_STATE_COUNT;\n    kr = thread_get_state(thread, THREAD_STATE_ID, (thread_state_t)&threadState,\n                          &count);\n    if (kr != KERN_SUCCESS) {\n      QBDI_ERROR(\"Failed to get thread state\\n\");\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n\n#if defined(QBDI_ARCH_X86) || defined(QBDI_ARCH_X86_64)\n    // x86 breakpoint quirk\n    threadState.THREAD_STATE_PC -= 1;\n#endif\n\n    // Setting thread state\n    count = THREAD_STATE_COUNT;\n    kr = thread_set_state(thread, THREAD_STATE_ID, (thread_state_t)&threadState,\n                          count);\n    if (kr != KERN_SUCCESS) {\n      QBDI_ERROR(\"Failed to set thread state\\n\");\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n\n    // Suspending thread\n    task_suspend(task);\n    // Set status\n    STATUS = Status::Stopped;\n  } else if (exception == EXC_CRASH) {\n    // Set status\n    STATUS = Status::Crashed;\n  }\n\n  // Signal new status\n  pthread_mutex_unlock(&STATUS_LOCK);\n  pthread_cond_signal(&STATUS_COND);\n\n  return KERN_SUCCESS;\n}\n\nDarwinProcess::DarwinProcess(pid_t process)\n    : pid(process), brk_address(nullptr), brk_value(0), suspended(true),\n      prot_page(0), prot_rx(true) {\n  kern_return_t kr;\n  thread_act_array_t threads;\n  mach_msg_type_number_t count;\n  struct kevent ke;\n\n  this->pageSize = getpagesize();\n\n  // Get task port\n  kr = task_for_pid(mach_task_self(), process, &this->task);\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to get task for pid {}, are you running as root?\",\n               (unsigned)process);\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  // Get thread ports\n  kr = task_threads(this->task, &threads, &count);\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to get task for pid {}, are you running as root?\",\n               (unsigned)process);\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  // Keep only the main thread (the preload doesn't support multithreading\n  // anyway)\n  this->mainThread = threads[0];\n  for (unsigned i = 1; i < count; i++) {\n    kr = mach_port_deallocate(mach_task_self(), threads[i]);\n    if (kr != KERN_SUCCESS) {\n      QBDI_ERROR(\"Failed to deallocate thread port\");\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n  }\n  kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)threads,\n                          count * sizeof(thread_act_t));\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to deallocate thread list\");\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n\n  // Setting up kernel queue to receive exit notification\n  this->kq = kqueue();\n  EV_SET(&ke, process, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);\n  if (kevent(this->kq, &ke, 1, NULL, 0, NULL) == -1) {\n    QBDI_ERROR(\"Failed to setup kqueue\");\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n\n  // Setting up pthread synchronization primitive\n  STATUS = Status::Running;\n\n  // Starting the exception handler\n  QBDI::qbdipreload_setup_exception_handler(\n      this->task, EXC_MASK_BREAKPOINT | EXC_MASK_CRASH, (void *)&onBreakpoint);\n}\n\nvoid DarwinProcess::suspend() {\n  kern_return_t kr;\n\n  if (this->suspended == false) {\n    kr = task_suspend(this->task);\n    if (kr != KERN_SUCCESS) {\n      QBDI_ERROR(\"Failed to suspend process: {}\", mach_error_string(kr));\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n    this->suspended = true;\n  }\n}\n\nvoid DarwinProcess::resume() {\n  kern_return_t kr;\n\n  if (this->suspended == true) {\n    kr = task_resume(this->task);\n    if (kr != KERN_SUCCESS) {\n      QBDI_ERROR(\"Failed to resume process: {}\", mach_error_string(kr));\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n    this->suspended = false;\n  }\n}\n\nvoid DarwinProcess::makeRW(void *address) {\n  kern_return_t kr;\n  QBDI::rword page =\n      (QBDI::rword)address - ((QBDI::rword)address % this->pageSize);\n\n  // We are setting another page as RW, RX the previous one\n  if (page != this->prot_page) {\n    makeRX();\n    // Switch to this new page\n    this->prot_page = page;\n  }\n  // The same page but it is in RX\n  if (this->prot_rx == true) {\n    kr = mach_vm_protect(this->task, (mach_vm_address_t)page, this->pageSize,\n                         false, VM_PROT_READ | VM_PROT_WRITE);\n    if (kr != KERN_SUCCESS) {\n      QBDI_ERROR(\n          \"Failed to change memory protection to RW of remote process: {}\",\n          mach_error_string(kr));\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n    this->prot_rx = false;\n  }\n}\n\nvoid DarwinProcess::makeRX() {\n  kern_return_t kr;\n\n  if (this->prot_rx == false) {\n    kr = mach_vm_protect(this->task, (mach_vm_address_t)this->prot_page,\n                         this->pageSize, false, VM_PROT_READ | VM_PROT_EXECUTE);\n    if (kr != KERN_SUCCESS) {\n      QBDI_ERROR(\n          \"Failed to change memory protection to RX of remote process: {}\",\n          mach_error_string(kr));\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n    this->prot_rx = true;\n  }\n}\n\nvoid DarwinProcess::setBreakpoint(void *address) {\n  mach_vm_size_t readSize = 0;\n  kern_return_t kr;\n\n  suspend();\n  makeRW(address);\n  kr = mach_vm_read_overwrite(this->task, (mach_vm_address_t)address,\n                              sizeof(uint8_t),\n                              (mach_vm_address_t) & this->brk_value, &readSize);\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to read remote process memory\");\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  kr = mach_vm_write(this->task, (mach_vm_address_t)address,\n                     (mach_vm_offset_t)&BRK_INS, sizeof(uint8_t));\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to write remote process memory\");\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n  this->brk_address = address;\n}\n\nvoid DarwinProcess::unsetBreakpoint() {\n  kern_return_t kr;\n\n  suspend();\n  makeRW(this->brk_address);\n  kr = mach_vm_write(this->task, (mach_vm_address_t)this->brk_address,\n                     (mach_vm_offset_t) & this->brk_value, sizeof(uint8_t));\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to write remote process memory\");\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n}\n\nvoid DarwinProcess::continueExecution() {\n  makeRX();\n  resume();\n}\n\nint DarwinProcess::waitForStatus() {\n  struct kevent ke;\n  timespec ts, zero = timespec{0, 0};\n\n  pthread_mutex_lock(&STATUS_LOCK);\n  // Wait for new status\n  while (STATUS == Status::Running) {\n    // Check for exit\n    if (kevent(this->kq, nullptr, 0, &ke, 1, &zero) == -1) {\n      QBDI_ERROR(\"Failed to poll the kqueue\");\n      exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n    }\n    if (ke.fflags & NOTE_EXIT) {\n      pthread_mutex_unlock(&STATUS_LOCK);\n      return (int)Status::Exited;\n    }\n    // Else wait on the status lock\n    clock_gettime(CLOCK_REALTIME, &ts);\n    ts.tv_nsec += 200;\n    pthread_cond_timedwait(&STATUS_COND, &STATUS_LOCK, &ts);\n  }\n  suspended = true;\n  Status ret = STATUS;\n  STATUS = Status::Running;\n  pthread_mutex_unlock(&STATUS_LOCK);\n  return (int)ret;\n}\n\nvoid DarwinProcess::getProcessGPR(QBDI::GPRState *gprState) {\n  kern_return_t kr;\n  THREAD_STATE threadState;\n  mach_msg_type_number_t count;\n\n  count = THREAD_STATE_COUNT;\n  kr = thread_get_state(this->mainThread, THREAD_STATE_ID,\n                        (thread_state_t)&threadState, &count);\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to get GPR thread state: {}\", mach_error_string(kr));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n\n  threadStateToGPRState(&threadState, gprState);\n}\n\nvoid DarwinProcess::getProcessFPR(QBDI::FPRState *fprState) {\n  kern_return_t kr;\n  THREAD_STATE_FP floatState;\n  mach_msg_type_number_t count;\n\n  count = THREAD_STATE_FP_COUNT;\n  kr = thread_get_state(this->mainThread, THREAD_STATE_FP_ID,\n                        (thread_state_t)&floatState, &count);\n  if (kr != KERN_SUCCESS) {\n    QBDI_ERROR(\"Failed to get FPR thread state: {}\", mach_error_string(kr));\n    exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n  }\n\n  floatStateToFPRState(&floatState, fprState);\n}\n\nbool hasExited(int status) { return status == Status::Exited; }\n\nbool hasStopped(int status) { return status == Status::Stopped; }\n\nbool hasCrashed(int status) { return status == Status::Crashed; }\n\nvoid threadStateToGPRState(THREAD_STATE *ts, QBDI::GPRState *gprState) {\n  return qbdipreload_threadCtxToGPRState((void *)ts, gprState);\n}\n\nvoid floatStateToFPRState(THREAD_STATE_FP *fs, QBDI::FPRState *fprState) {\n  return qbdipreload_floatCtxToFPRState((void *)fs, fprState);\n}\n"
  },
  {
    "path": "tools/validator/darwin_process.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef DARWIN_PROCESS_H\n#define DARWIN_PROCESS_H\n\n#include \"process.h\"\n\n#include <mach/task.h>\n#include <stdint.h>\n\nstatic const uint8_t BRK_INS = 0xCC;\n\n#if defined(QBDI_ARCH_X86)\n#define THREAD_STATE_ID x86_THREAD_STATE32\n#define THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT\n#define THREAD_STATE_FP_ID x86_FLOAT_STATE32\n#define THREAD_STATE_FP_COUNT x86_FLOAT_STATE32_COUNT\n#define THREAD_STATE x86_thread_state32_t\n#define THREAD_STATE_FP x86_float_state32_t\n#define THREAD_STATE_BP __ebp\n#define THREAD_STATE_SP __esp\n#define THREAD_STATE_PC __eip\n#elif defined(QBDI_ARCH_X86_64)\n#define THREAD_STATE_ID x86_THREAD_STATE64\n#define THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT\n#define THREAD_STATE_FP_ID x86_FLOAT_STATE64\n#define THREAD_STATE_FP_COUNT x86_FLOAT_STATE64_COUNT\n#define THREAD_STATE x86_thread_state64_t\n#define THREAD_STATE_FP x86_float_state64_t\n#define THREAD_STATE_BP __rbp\n#define THREAD_STATE_SP __rsp\n#define THREAD_STATE_PC __rip\n#elif defined(QBDI_ARCH_AARCH64)\n#define THREAD_STATE_ID ARM_THREAD_STATE64\n#define THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT\n#define THREAD_STATE_FP_ID ARM_NEON_STATE64\n#define THREAD_STATE_FP_COUNT ARM_NEON_STATE64_COUNT\n#define THREAD_STATE arm_thread_state64_t\n#define THREAD_STATE_FP arm_neon_state64_t\n\nstatic inline QBDI::rword getSP(THREAD_STATE *state) {\n  return __darwin_arm_thread_state64_get_sp(*state);\n}\n\nstatic inline void setSP(THREAD_STATE *state, QBDI::rword address) {\n  __darwin_arm_thread_state64_set_sp(*state, (void *)address);\n}\n\nstatic inline void setBP(THREAD_STATE *state, QBDI::rword address) {\n  __darwin_arm_thread_state64_set_fp(*state, (void *)address);\n}\n#endif\n\nclass DarwinProcess : public Process {\n\nprivate:\n  pid_t pid;\n  task_t task;\n  thread_act_t mainThread;\n  void *brk_address;\n  uint8_t brk_value;\n  bool suspended;\n  QBDI::rword pageSize;\n  QBDI::rword prot_page;\n  bool prot_rx;\n  int kq;\n\n  void suspend();\n\n  void resume();\n\n  void makeRW(void *address);\n\n  void makeRX();\n\npublic:\n  DarwinProcess(pid_t process);\n\n  pid_t getPID() { return pid; }\n\n  void setBreakpoint(void *address);\n\n  void unsetBreakpoint();\n\n  void continueExecution();\n\n  int waitForStatus();\n\n  void getProcessGPR(QBDI::GPRState *gprState);\n\n  void getProcessFPR(QBDI::FPRState *fprState);\n};\n\nvoid threadStateToGPRState(THREAD_STATE *ts, QBDI::GPRState *gprState);\n\nvoid floatStateToFPRState(THREAD_STATE_FP *fs, QBDI::FPRState *fprState);\n\n#endif // DARWIN_PROCESS_H\n"
  },
  {
    "path": "tools/validator/darwin_validator.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"darwin_process.h\"\n#include \"instrumented.h\"\n#include \"master.h\"\n#include \"QBDIPreload.h\"\n\n#include <mach/mach.h>\n#include <mach/task.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"Utility/LogSys.h\"\n\nstatic const size_t STACK_SIZE = 8388608;\nstatic QBDI::GPRState ENTRY_GPR;\nstatic QBDI::FPRState ENTRY_FPR;\nstatic pid_t DEBUGGED, INSTRUMENTED;\nint ctrlfd, datafd, outputDBIfd, outputDBGfd;\n\nenum Role { Master, Instrumented, Debugged } ROLE;\n\nQBDIPRELOAD_INIT;\n\nint QBDI::qbdipreload_on_main(int argc, char **argv) {\n  QBDI::setLogPriority(QBDI::LogPriority::DEBUG);\n\n  if (ROLE == Role::Master) {\n    DarwinProcess *debuggedProcess = new DarwinProcess(DEBUGGED);\n    start_master(debuggedProcess, INSTRUMENTED, ctrlfd, datafd, outputDBGfd,\n                 outputDBIfd);\n    delete debuggedProcess;\n  } else if (ROLE == Role::Instrumented) {\n    QBDI::VM *vm = new QBDI::VM();\n    vm->instrumentAllExecutableMaps();\n\n    // Skip ourself, the loader, system library (to avoid conflicts) and objc\n    // (to avoid awful performances)\n    for (const QBDI::MemoryMap &m : QBDI::getCurrentProcessMaps()) {\n      if ((m.name.compare(0, 9, \"libsystem\") == 0 ||\n           m.name.compare(0, 4, \"dyld\") == 0 ||\n           m.name.compare(0, 7, \"libdyld\") == 0 ||\n           m.name.compare(0, 7, \"libobjc\") == 0 ||\n           m.name.compare(0, 13, \"libvalidator2\") == 0)) {\n        vm->removeInstrumentedRange(m.range.start(), m.range.end());\n      }\n    }\n\n    vm->setGPRState(&ENTRY_GPR);\n    vm->setFPRState(&ENTRY_FPR);\n\n    QBDI::rword start = QBDI_GPR_GET(vm->getGPRState(), QBDI::REG_PC);\n    QBDI::rword stop =\n        *((QBDI::rword *)QBDI_GPR_GET(vm->getGPRState(), QBDI::REG_SP));\n    start_instrumented(vm, start, stop, ctrlfd, datafd);\n  }\n  exit(0);\n}\n\nint QBDI::qbdipreload_on_premain(void *gprCtx, void *fpuCtx) {\n  THREAD_STATE *threadState = (THREAD_STATE *)gprCtx;\n  THREAD_STATE_FP *floatState = (THREAD_STATE_FP *)fpuCtx;\n\n  // Perform stack swapping and GPR / FPR init for Instrumented only\n  if (ROLE == Role::Instrumented) {\n    // Allocating fake stack\n    void *newStack = QBDI::alignedAlloc(STACK_SIZE, 16);\n    if (newStack == nullptr) {\n      fprintf(stderr, \"Failed to allocate fake stack\\n\");\n      exit(QBDIPRELOAD_ERR_STARTUP_FAILED);\n    }\n\n    // Copying the initial thread state\n    threadStateToGPRState(threadState, &ENTRY_GPR);\n    floatStateToFPRState(floatState, &ENTRY_FPR);\n\n    // Swapping to fake stack\n#if defined(QBDI_ARCH_X86)\n    threadState->THREAD_STATE_BP = (rword)newStack + STACK_SIZE - 8;\n    threadState->THREAD_STATE_SP = threadState->THREAD_STATE_BP - 44;\n    memcpy((void *)threadState->THREAD_STATE_SP, (void *)ENTRY_GPR.esp, 44);\n#elif defined(QBDI_ARCH_X86_64)\n    threadState->THREAD_STATE_BP = (rword)newStack + STACK_SIZE - 8;\n    threadState->THREAD_STATE_SP = threadState->THREAD_STATE_BP;\n#else\n    setBP(threadState, (rword)newStack + STACK_SIZE - 8);\n    setSP(threadState, (rword)newStack + STACK_SIZE - 8);\n#endif\n  } else if (ROLE == Role::Master) {\n    // LC_UNIXTHREAD binaries use a different calling convention\n    // This allow to call catchEntrypoint, and have not side effect\n    // as original execution is never resumed in Master mode\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n    auto sp = threadState->THREAD_STATE_SP;\n#else\n    auto sp = getSP(threadState);\n#endif\n    if ((sp & 0x8) == 0) {\n      sp -= 8;\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n      threadState->THREAD_STATE_SP = sp;\n#else\n      setSP(threadState, sp);\n#endif\n    }\n  }\n\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint QBDI::qbdipreload_on_run(QBDI::VMInstanceRef vm, QBDI::rword start,\n                             QBDI::rword stop) {\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nint QBDI::qbdipreload_on_exit(int status) {\n  if (ROLE == Role::Instrumented) {\n    cleanup_instrumentation();\n  }\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint QBDI::qbdipreload_on_start(void *main) {\n  int ctrlfds[2];\n  int datafds[2];\n  int outputDBIfds[2];\n  int outputDBGfds[2];\n  int dummyfds[2];\n  if (pipe(ctrlfds) != 0 or pipe(datafds) != 0 or pipe(outputDBIfds) != 0 or\n      pipe(outputDBGfds) != 0 or pipe(dummyfds) != 0) {\n    fprintf(stderr,\n            \"validator: fatal error, fail create pipe for intrumented process \"\n            \"!\\n\\n\");\n    exit(0);\n  }\n\n  INSTRUMENTED = fork();\n  if (INSTRUMENTED == 0) {\n\n    ROLE = Role::Instrumented;\n    ctrlfd = ctrlfds[0];\n    datafd = datafds[1];\n    close(ctrlfds[1]);\n    close(datafds[0]);\n    if (dup2(outputDBIfds[1], 1) == -1) {\n      perror(\"instrumented: fail to dup2\");\n    }\n    close(outputDBIfds[0]);\n    close(outputDBIfds[1]);\n    close(outputDBGfds[0]);\n    close(outputDBGfds[1]);\n    close(dummyfds[0]);\n    close(dummyfds[1]);\n\n    QBDI::qbdipreload_hook_main(main);\n    return QBDIPRELOAD_NO_ERROR;\n  }\n\n  DEBUGGED = fork();\n  if (DEBUGGED == 0) {\n\n    ROLE = Role::Debugged;\n    if (dup2(dummyfds[0], ctrlfds[0]) == -1 or\n        dup2(dummyfds[1], datafds[1]) == -1 or dup2(outputDBGfds[1], 1) == -1) {\n      perror(\"debugged: fail to dup2\");\n    }\n    close(ctrlfds[1]);\n    close(datafds[0]);\n    close(outputDBIfds[0]);\n    close(outputDBIfds[1]);\n    close(outputDBGfds[0]);\n    close(outputDBGfds[1]);\n    close(dummyfds[0]);\n    close(dummyfds[1]);\n\n    // We put ourself to sleep, waiting for our charming prince\n    task_suspend(mach_task_self());\n    return QBDIPRELOAD_NO_ERROR;\n  }\n\n  ctrlfd = ctrlfds[1];\n  datafd = datafds[0];\n  outputDBIfd = outputDBIfds[0];\n  outputDBGfd = outputDBGfds[0];\n  close(ctrlfds[0]);\n  close(datafds[1]);\n  close(outputDBIfds[1]);\n  close(outputDBGfds[1]);\n  close(dummyfds[0]);\n  close(dummyfds[1]);\n\n  QBDI::qbdipreload_hook_main(main);\n  ROLE = Role::Master;\n\n  return QBDIPRELOAD_NO_ERROR;\n}\n"
  },
  {
    "path": "tools/validator/instrumented.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <dlfcn.h>\n#include <errno.h>\n#include <inttypes.h>\n#include <set>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"instrumented.h\"\n#include \"pipes.h\"\n\n#include <QBDI.h>\n#include \"Utility/LogSys.h\"\n\nint SAVED_ERRNO = 0;\n\nstruct Pipes {\n  FILE *ctrlPipe;\n  FILE *dataPipe;\n};\n\nstruct CBData {\n  Pipes p;\n  unsigned countIgnoreInst;\n};\n\nstatic QBDI::VMAction step(QBDI::VMInstanceRef vm, QBDI::GPRState *gprState,\n                           QBDI::FPRState *fprState, void *data) {\n  COMMAND cmd;\n  SAVED_ERRNO = errno;\n  CBData *cbdata = (CBData *)data;\n\n  const QBDI::InstAnalysis *instAnalysis = vm->getInstAnalysis(\n      QBDI::ANALYSIS_INSTRUCTION | QBDI::ANALYSIS_DISASSEMBLY);\n  // Write a new instruction event\n  if (writeInstructionEvent(instAnalysis->address, instAnalysis->mnemonic,\n                            instAnalysis->disassembly, gprState, fprState,\n                            cbdata->countIgnoreInst != 0,\n                            cbdata->p.dataPipe) != 1) {\n    // DATA pipe failure, we exit\n    QBDI_ERROR(\"Lost the data pipe, exiting!\");\n    return QBDI::VMAction::STOP;\n  }\n  if (cbdata->countIgnoreInst > 0) {\n    cbdata->countIgnoreInst -= 1;\n  }\n#if defined(QBDI_ARCH_ARM)\n  if (strcmp(instAnalysis->mnemonic, \"t2IT\") == 0) {\n    instAnalysis = vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION |\n                                       QBDI::ANALYSIS_DISASSEMBLY |\n                                       QBDI::ANALYSIS_OPERANDS);\n    if (instAnalysis->numOperands < 2 or\n        instAnalysis->operands[1].type != QBDI::OperandType::OPERAND_IMM) {\n\n      QBDI_ERROR(\"Fail to parse IT instructions\");\n      return QBDI::VMAction::STOP;\n    }\n    QBDI::rword itType = instAnalysis->operands[1].value;\n    if ((itType & 1) != 0) {\n      cbdata->countIgnoreInst += 4;\n    } else if ((itType & 2) != 0) {\n      cbdata->countIgnoreInst += 3;\n    } else if ((itType & 4) != 0) {\n      cbdata->countIgnoreInst += 2;\n    } else if ((itType & 8) != 0) {\n      cbdata->countIgnoreInst += 1;\n    } else {\n      QBDI_ERROR(\"Invalid IT instruction\");\n      return QBDI::VMAction::STOP;\n    }\n  }\n#endif\n  // Read next command\n  if (readCommand(&cmd, cbdata->p.ctrlPipe) != 1) {\n    // CTRL pipe failure, we exit\n    QBDI_ERROR(\"Lost the control pipe, exiting!\");\n    return QBDI::VMAction::STOP;\n  }\n\n  errno = SAVED_ERRNO;\n  if (cmd == COMMAND::CONTINUE) {\n    // Signaling the VM to continue the execution\n    return QBDI::VMAction::CONTINUE;\n  } else if (cmd == COMMAND::STOP) {\n    // Signaling the VM to stop the execution\n    return QBDI::VMAction::STOP;\n  }\n  // FATAL\n  QBDI_ERROR(\"Did not recognize command from the control pipe, exiting!\");\n  return QBDI::VMAction::STOP;\n}\n\nstatic QBDI::VMAction verifyMemoryAccess(QBDI::VMInstanceRef vm,\n                                         QBDI::GPRState *gprState,\n                                         QBDI::FPRState *fprState, void *data) {\n  SAVED_ERRNO = errno;\n  CBData *cbdata = (CBData *)data;\n\n  const QBDI::InstAnalysis *instAnalysis =\n      vm->getInstAnalysis(QBDI::ANALYSIS_INSTRUCTION);\n\n  bool mayRead = instAnalysis->mayLoad_LLVM;\n  bool mayWrite = instAnalysis->mayStore_LLVM;\n\n  bool doRead = false;\n  bool doWrite = false;\n  std::vector<QBDI::MemoryAccess> accesses = vm->getInstMemoryAccess();\n  for (auto &m : accesses) {\n    if ((m.type & QBDI::MEMORY_READ) != 0) {\n      doRead = true;\n    }\n    if ((m.type & QBDI::MEMORY_WRITE) != 0) {\n      doWrite = true;\n    }\n  }\n\n  // llvm API mayRead and mayWrite are incomplete.\n  bool bypassRead = false;\n  bool bypassWrite = false;\n\n  if (doRead and !mayRead) {\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n    // all return instructions read the return address.\n    bypassRead |= instAnalysis->isReturn;\n    const std::set<std::string> shouldReadInsts{\n        \"ARPL16mr\", \"BOUNDS16rm\", \"BOUNDS32rm\",    \"CMPSB\",   \"CMPSL\",\n        \"CMPSQ\",    \"CMPSW\",      \"LODSB\",         \"LODSL\",   \"LODSQ\",\n        \"LODSW\",    \"LRETI32\",    \"LRETI64\",       \"LRETI16\", \"LRET32\",\n        \"LRET64\",   \"LRET16\",     \"MOVDIR64B16\",   \"MOVSB\",   \"MOVSL\",\n        \"MOVSQ\",    \"MOVSW\",      \"OR32mi8Locked\", \"RCL16m1\", \"RCL16mCL\",\n        \"RCL16mi\",  \"RCL32m1\",    \"RCL32mCL\",      \"RCL32mi\", \"RCL64m1\",\n        \"RCL64mCL\", \"RCL64mi\",    \"RCL8m1\",        \"RCL8mCL\", \"RCL8mi\",\n        \"RCR16m1\",  \"RCR16mCL\",   \"RCR16mi\",       \"RCR32m1\", \"RCR32mCL\",\n        \"RCR32mi\",  \"RCR64m1\",    \"RCR64mCL\",      \"RCR64mi\", \"RCR8m1\",\n        \"RCR8mCL\",  \"RCR8mi\",     \"RETI32\",        \"RETI64\",  \"RETI16\",\n        \"RET32\",    \"RET64\",      \"RET16\",         \"SCASB\",   \"SCASL\",\n        \"SCASQ\",    \"SCASW\",\n    };\n    bypassRead |= (shouldReadInsts.count(instAnalysis->mnemonic) == 1);\n#elif defined(QBDI_ARCH_AARCH64)\n    const std::set<std::string> shouldReadInsts{\n        \"LD64B\",\n    };\n    bypassRead |= (shouldReadInsts.count(instAnalysis->mnemonic) == 1);\n#endif\n  } else if (!doRead and mayRead) {\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n    const std::set<std::string> noReadInsts{\n        \"CLDEMOTE\",    \"CLFLUSH\",      \"CLFLUSHOPT\", \"CLWB\",\n        \"FEMMS\",       \"FXSAVE\",       \"FXSAVE64\",   \"INT\",\n        \"INT3\",        \"LFENCE\",       \"LOADIWKEY\",  \"MFENCE\",\n        \"MMX_EMMS\",    \"MMX_MOVNTQmr\", \"MOVDIRI32\",  \"MOVDIRI64\",\n        \"MWAITrr\",     \"PAUSE\",        \"PREFETCH\",   \"PREFETCHNTA\",\n        \"PREFETCHT0\",  \"PREFETCHT1\",   \"PREFETCHT2\", \"PREFETCHW\",\n        \"PREFETCHWT1\", \"PTWRITE64r\",   \"PTWRITEr\",   \"RDFSBASE\",\n        \"RDFSBASE64\",  \"RDGSBASE\",     \"RDGSBASE64\", \"RDPID32\",\n        \"SERIALIZE\",   \"SFENCE\",       \"STTILECFG\",  \"TILERELEASE\",\n        \"TRAP\",        \"UMONITOR16\",   \"UMONITOR32\", \"UMONITOR64\",\n        \"WRFSBASE\",    \"WRFSBASE64\",   \"WRGSBASE\",   \"WRGSBASE64\",\n        \"XSETBV\",\n    };\n    bypassRead |= (noReadInsts.count(instAnalysis->mnemonic) == 1);\n#elif defined(QBDI_ARCH_AARCH64)\n    const std::set<std::string> noReadInsts{\n        \"CLREX\",  \"STLXPW\", \"STLXPX\", \"STLXRB\", \"STLXRH\", \"STLXRW\",\n        \"STLXRX\", \"STXPW\",  \"STXPX\",  \"STXRB\",  \"STXRH\",  \"STXRW\",\n        \"STXRX\",  \"DMB\",    \"DSB\",    \"HINT\",   \"ISB\",\n    };\n    bypassRead |= (noReadInsts.count(instAnalysis->mnemonic) == 1);\n#endif\n  }\n\n  if (doWrite and !mayWrite) {\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n    // all call instructions write the return address.\n    bypassWrite |= instAnalysis->isCall;\n    const std::set<std::string> shouldWriteInsts{\n        \"CALL16m\",     \"CALL16m_NT\",    \"CALL16r\",       \"CALL16r_NT\",\n        \"CALL32m\",     \"CALL32m_NT\",    \"CALL32r\",       \"CALL32r_NT\",\n        \"CALL64m\",     \"CALL64m_NT\",    \"CALL64pcrel32\", \"CALL64r\",\n        \"CALL64r_NT\",  \"CALLpcrel16\",   \"CALLpcrel32\",   \"ENTER\",\n        \"MOVDIR64B16\", \"MOVSB\",         \"MOVSL\",         \"MOVSQ\",\n        \"MOVSW\",       \"OR32mi8Locked\", \"STOSB\",         \"STOSL\",\n        \"STOSQ\",       \"STOSW\",\n    };\n    bypassWrite |= (shouldWriteInsts.count(instAnalysis->mnemonic) == 1);\n#elif defined(QBDI_ARCH_AARCH64)\n    const std::set<std::string> shouldWriteInsts{\n        \"ST64B\",\n        \"ST64BV\",\n        \"ST64BV0\",\n    };\n    bypassWrite |= (shouldWriteInsts.count(instAnalysis->mnemonic) == 1);\n#endif\n  } else if (!doWrite and mayWrite) {\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n    const std::set<std::string> noWriteInsts{\n        \"CLDEMOTE\",    \"CLFLUSH\",    \"CLFLUSHOPT\", \"CLWB\",        \"FEMMS\",\n        \"FXRSTOR\",     \"FXRSTOR64\",  \"INT\",        \"INT3\",        \"LDMXCSR\",\n        \"LDTILECFG\",   \"LFENCE\",     \"LOADIWKEY\",  \"MFENCE\",      \"MMX_EMMS\",\n        \"MWAITrr\",     \"PAUSE\",      \"PREFETCH\",   \"PREFETCHNTA\", \"PREFETCHT0\",\n        \"PREFETCHT1\",  \"PREFETCHT2\", \"PREFETCHW\",  \"PREFETCHWT1\", \"PTWRITE64m\",\n        \"PTWRITE64r\",  \"PTWRITEm\",   \"PTWRITEr\",   \"RDFSBASE\",    \"RDFSBASE64\",\n        \"RDGSBASE\",    \"RDGSBASE64\", \"RDPID32\",    \"SERIALIZE\",   \"SFENCE\",\n        \"TILERELEASE\", \"UMONITOR16\", \"UMONITOR32\", \"UMONITOR64\",  \"VLDMXCSR\",\n        \"WRFSBASE\",    \"WRFSBASE64\", \"WRGSBASE\",   \"WRGSBASE64\",  \"XRSTOR\",\n        \"XRSTOR64\",    \"XRSTORS\",    \"XRSTORS64\",  \"XSETBV\",\n    };\n    bypassWrite |= (noWriteInsts.count(instAnalysis->mnemonic) == 1);\n#elif defined(QBDI_ARCH_AARCH64)\n    const std::set<std::string> noWriteInsts{\n        \"LDXRB\",  \"LDXRH\",  \"LDXRW\", \"LDXRX\", \"LDAXRB\", \"LDAXRH\",\n        \"LDAXRW\", \"LDAXRX\", \"LDXPW\", \"LDXPX\", \"LDAXPW\", \"LDAXPX\",\n        \"CLREX\",  \"DMB\",    \"DSB\",   \"HINT\",  \"ISB\",\n    };\n    bypassWrite |= (noWriteInsts.count(instAnalysis->mnemonic) == 1);\n#endif\n  }\n\n  if ((doRead == mayRead || bypassRead) &&\n      (doWrite == mayWrite || bypassWrite)) {\n    errno = SAVED_ERRNO;\n    return QBDI::VMAction::CONTINUE;\n  }\n\n  // Write a new instruction event\n  if (writeMismatchMemAccessEvent(\n          instAnalysis->address, doRead, instAnalysis->mayLoad, doWrite,\n          instAnalysis->mayStore, accesses, cbdata->p.dataPipe) != 1) {\n    // DATA pipe failure, we exit\n    QBDI_ERROR(\"Lost the data pipe, exiting!\");\n    return QBDI::VMAction::STOP;\n  }\n  errno = SAVED_ERRNO;\n  // Continue the execution\n  return QBDI::VMAction::CONTINUE;\n}\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\nstatic QBDI::VMAction logSyscall(QBDI::VMInstanceRef vm,\n                                 QBDI::GPRState *gprState,\n                                 QBDI::FPRState *fprState, void *data) {\n  CBData *cbdata = (CBData *)data;\n  // We don't have the address, it just need to be different from 0\n  writeExecTransferEvent(1, cbdata->p.dataPipe);\n  return QBDI::VMAction::CONTINUE;\n}\n#endif\n\nstatic QBDI::VMAction logTransfer(QBDI::VMInstanceRef vm,\n                                  const QBDI::VMState *state,\n                                  QBDI::GPRState *gprState,\n                                  QBDI::FPRState *fprState, void *data) {\n  CBData *cbdata = (CBData *)data;\n  writeExecTransferEvent(state->basicBlockStart, cbdata->p.dataPipe);\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction saveErrno(QBDI::VMInstanceRef vm,\n                                const QBDI::VMState *state,\n                                QBDI::GPRState *gprState,\n                                QBDI::FPRState *fprState, void *data) {\n  SAVED_ERRNO = errno;\n  return QBDI::VMAction::CONTINUE;\n}\n\nstatic QBDI::VMAction restoreErrno(QBDI::VMInstanceRef vm,\n                                   const QBDI::VMState *state,\n                                   QBDI::GPRState *gprState,\n                                   QBDI::FPRState *fprState, void *data) {\n  errno = SAVED_ERRNO;\n  return QBDI::VMAction::CONTINUE;\n}\n\nQBDI::VM *VM;\nCBData CBDATA = {{NULL, NULL}, 0};\n\nvoid cleanup_instrumentation() {\n  static bool cleaned_up = false;\n  if (cleaned_up == false) {\n    writeEvent(EVENT::EXIT, CBDATA.p.dataPipe);\n    fclose(CBDATA.p.ctrlPipe);\n    fclose(CBDATA.p.dataPipe);\n    delete VM;\n    cleaned_up = true;\n  }\n}\n\nvoid start_instrumented(QBDI::VM *vm, QBDI::rword start, QBDI::rword stop,\n                        int ctrlfd, int datafd) {\n\n  VM = vm;\n  if (getenv(\"QBDI_DEBUG\") != NULL) {\n    QBDI::setLogPriority(QBDI::LogPriority::DEBUG);\n  } else {\n    QBDI::setLogPriority(QBDI::LogPriority::ERROR);\n  }\n  // Opening communication FIFO\n  CBDATA.p.ctrlPipe = fdopen(ctrlfd, \"rb\");\n  CBDATA.p.dataPipe = fdopen(datafd, \"wb\");\n  if (CBDATA.p.ctrlPipe == nullptr || CBDATA.p.dataPipe == nullptr) {\n    QBDI_ERROR(\"Could not open communication pipes with master, exiting!\");\n    return;\n  }\n\n  vm->addCodeCB(QBDI::PREINST, step, (void *)&CBDATA);\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86) || \\\n    defined(QBDI_ARCH_AARCH64)\n  // memory Access are not supported for ARM now\n  vm->recordMemoryAccess(QBDI::MEMORY_READ_WRITE);\n  vm->addCodeCB(QBDI::POSTINST, verifyMemoryAccess, (void *)&CBDATA);\n#endif\n\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  vm->addMnemonicCB(\"syscall\", QBDI::POSTINST, logSyscall, (void *)&CBDATA);\n#endif\n  vm->addVMEventCB(QBDI::VMEvent::EXEC_TRANSFER_CALL, logTransfer,\n                   (void *)&CBDATA);\n  vm->addVMEventCB(QBDI::VMEvent::EXEC_TRANSFER_CALL |\n                       QBDI::VMEvent::BASIC_BLOCK_ENTRY,\n                   restoreErrno, nullptr);\n  vm->addVMEventCB(QBDI::VMEvent::EXEC_TRANSFER_RETURN |\n                       QBDI::VMEvent::BASIC_BLOCK_EXIT,\n                   saveErrno, nullptr);\n\n  vm->run(start, stop);\n\n  cleanup_instrumentation();\n}\n"
  },
  {
    "path": "tools/validator/instrumented.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef INSTRUMENTED_H\n#define INSTRUMENTED_H\n\n#include <QBDI/VM.h>\n\nvoid start_instrumented(QBDI::VM *vm, QBDI::rword start, QBDI::rword stop,\n                        int ctrlfd, int datafd);\n\nvoid cleanup_instrumentation();\n\n#endif // INSTRUMENTED_H\n"
  },
  {
    "path": "tools/validator/linux_process.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"linux_process.h\"\n#include \"validator.h\"\n\n#include <errno.h>\n#include <signal.h>\n#include <string.h>\n#include <sys/ptrace.h>\n#include <sys/user.h>\n#include <sys/wait.h>\n#include \"Utility/LogSys.h\"\n\n#ifndef QBDI_ARCH_ARM\nvoid LinuxProcess::setBreakpoint(void *address) {\n  this->brk_address = address;\n  this->brk_value = ptrace(PTRACE_PEEKDATA, this->pid, address, NULL);\n  if (ptrace(PTRACE_POKEDATA, this->pid, address,\n             BRK_INS | (this->brk_value & (~BRK_MASK))) == -1) {\n    QBDI_ERROR(\"Failed to set breakpoint: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n}\n#endif\n\nvoid LinuxProcess::unsetBreakpoint() {\n  if (ptrace(PTRACE_POKEDATA, this->pid, this->brk_address, this->brk_value) ==\n      -1) {\n    QBDI_ERROR(\"Failed to unset breakpoint: {}\", strerror(errno));\n    exit(VALIDATOR_ERR_UNEXPECTED_API_FAILURE);\n  }\n}\n\nvoid LinuxProcess::continueExecution() {\n  ptrace(PTRACE_CONT, this->pid, NULL, NULL);\n}\n\nint LinuxProcess::waitForStatus() {\n  int status = 0;\n  waitpid(this->pid, &status, 0);\n#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)\n  if (WSTOPSIG(status) == SIGBRK) {\n    GPR_STRUCT user;\n    ptrace(PTRACE_GETREGS, this->pid, NULL, &user);\n    fix_GPR_STRUCT(&user);\n    ptrace(PTRACE_SETREGS, this->pid, NULL, &user);\n  }\n#endif\n  return status;\n}\n\nbool hasExited(int status) { return WIFEXITED(status) > 0; }\n\nbool hasStopped(int status) {\n  return WSTOPSIG(status) == SIGBRK || WSTOPSIG(status) == SIGSTOP;\n}\n\nbool hasCrashed(int status) {\n  return hasExited(status) == false && hasStopped(status) == false;\n}\n"
  },
  {
    "path": "tools/validator/linux_process.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef LINUX_PROCESS_H\n#define LINUX_PROCESS_H\n\n#include \"process.h\"\n\n#if defined(QBDI_ARCH_X86_64)\n#include \"X86_64/linux_X86_64.h\"\n#elif defined(QBDI_ARCH_X86)\n#include \"X86/linux_X86.h\"\n#elif defined(QBDI_ARCH_ARM)\n#include \"ARM/linux_ARM.h\"\n#elif defined(QBDI_ARCH_AARCH64)\n#include \"AARCH64/linux_AARCH64.h\"\n#endif\n\nclass LinuxProcess : public Process {\n\nprivate:\n  pid_t pid;\n  void *brk_address;\n  long brk_value;\n\npublic:\n  LinuxProcess(pid_t process)\n      : pid(process), brk_address(nullptr), brk_value(0) {}\n\n  pid_t getPID() { return pid; }\n\n  void setBreakpoint(void *address);\n\n  void unsetBreakpoint();\n\n  void continueExecution();\n\n  int waitForStatus();\n\n  void getProcessGPR(QBDI::GPRState *gprState);\n\n  void getProcessFPR(QBDI::FPRState *fprState);\n};\n\n#endif // LINUX_PROCESS_H\n"
  },
  {
    "path": "tools/validator/linux_validator.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"instrumented.h\"\n#include \"linux_process.h\"\n#include \"master.h\"\n#include \"validator.h\"\n#include \"QBDIPreload.h\"\n\n#include <dlfcn.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/ptrace.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include \"QBDI/Memory.hpp\"\n#include \"QBDI/Platform.h\"\n#include \"Utility/LogSys.h\"\n\nstatic bool INSTRUMENTED = false;\nstatic bool MASTER = false;\nstatic pid_t debugged, instrumented;\nint ctrlfd, datafd, outputDBIfd, outputDBGfd;\n\nQBDIPRELOAD_INIT;\n\nint QBDI::qbdipreload_on_main(int argc, char **argv) {\n  if (INSTRUMENTED) {\n    if (getenv(\"QBDI_DEBUG\") != NULL) {\n      QBDI::setLogPriority(QBDI::LogPriority::DEBUG);\n    } else {\n      QBDI::setLogPriority(QBDI::LogPriority::WARNING);\n    }\n\n    return QBDIPRELOAD_NOT_HANDLED;\n  } else {\n    LinuxProcess *debuggedProcess = nullptr;\n    debuggedProcess = new LinuxProcess(debugged);\n    start_master(debuggedProcess, instrumented, ctrlfd, datafd, outputDBGfd,\n                 outputDBIfd);\n    delete debuggedProcess;\n    return QBDIPRELOAD_NO_ERROR;\n  }\n}\n\nint QBDI::qbdipreload_on_premain(void *gprCtx, void *fpuCtx) {\n  if (INSTRUMENTED)\n    return QBDIPRELOAD_NOT_HANDLED;\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint QBDI::qbdipreload_on_run(QBDI::VMInstanceRef vm, QBDI::rword start,\n                             QBDI::rword stop) {\n  start_instrumented(vm, start, stop, ctrlfd, datafd);\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n\nvoid killChild() {\n  kill(debugged, SIGKILL);\n  kill(instrumented, SIGKILL);\n}\n\nint QBDI::qbdipreload_on_exit(int status) {\n  if (INSTRUMENTED) {\n    cleanup_instrumentation();\n  } else if (MASTER) {\n    killChild();\n  }\n  return QBDIPRELOAD_NO_ERROR;\n}\n\nint QBDI::qbdipreload_on_start(void *main) {\n  setvbuf(stdin, NULL, _IONBF, 0);\n  setvbuf(stdout, NULL, _IONBF, 0);\n  setvbuf(stderr, NULL, _IONBF, 0);\n\n  int ctrlfds[2];\n  int datafds[2];\n  int outputDBIfds[2];\n  int outputDBGfds[2];\n  int dummyfds[2];\n  if (pipe(ctrlfds) != 0 or pipe(datafds) != 0 or pipe(outputDBIfds) != 0 or\n      pipe(outputDBGfds) != 0 or pipe(dummyfds) != 0) {\n    fprintf(stderr,\n            \"validator: fatal error, fail create pipe for intrumented process \"\n            \"!\\n\\n\");\n    exit(0);\n  }\n\n  instrumented = fork();\n  if (instrumented == 0) {\n\n    ctrlfd = ctrlfds[0];\n    datafd = datafds[1];\n    close(ctrlfds[1]);\n    close(datafds[0]);\n    if (dup2(outputDBIfds[1], 1) == -1) {\n      perror(\"instrumented: fail to dup2\");\n    }\n    close(outputDBIfds[0]);\n    close(outputDBIfds[1]);\n    close(outputDBGfds[0]);\n    close(outputDBGfds[1]);\n    close(dummyfds[0]);\n    close(dummyfds[1]);\n\n    signal(SIGCHLD, SIG_IGN);\n    INSTRUMENTED = true;\n    return QBDIPRELOAD_NOT_HANDLED;\n  }\n\n  debugged = fork();\n  if (debugged == 0) {\n    // we want to keep the same number of filedescriptor\n    if (dup2(dummyfds[0], ctrlfds[0]) == -1 or\n        dup2(dummyfds[1], datafds[1]) == -1 or dup2(outputDBGfds[1], 1) == -1) {\n      perror(\"debugged: fail to dup2\");\n    }\n    close(ctrlfds[1]);\n    close(datafds[0]);\n    close(outputDBIfds[0]);\n    close(outputDBIfds[1]);\n    close(outputDBGfds[0]);\n    close(outputDBGfds[1]);\n    close(dummyfds[0]);\n    close(dummyfds[1]);\n\n    // to keep the same number of filedescriptor, open a dummy pipe\n\n    signal(SIGCHLD, SIG_IGN);\n\n    ptrace(PTRACE_TRACEME, 0, NULL, NULL);\n    raise(SIGSTOP);\n    return QBDIPRELOAD_NO_ERROR;\n  }\n\n  if (ptrace(PTRACE_ATTACH, debugged, NULL, NULL) == -1) {\n    fprintf(stderr, \"validator: fatal error, PTRACE_ATTACH failed !\\n\\n\");\n    kill(instrumented, SIGKILL);\n    kill(debugged, SIGKILL);\n    exit(0);\n  }\n\n  ctrlfd = ctrlfds[1];\n  datafd = datafds[0];\n  outputDBIfd = outputDBIfds[0];\n  outputDBGfd = outputDBGfds[0];\n  close(ctrlfds[0]);\n  close(datafds[1]);\n  close(outputDBIfds[1]);\n  close(outputDBGfds[1]);\n  close(dummyfds[0]);\n  close(dummyfds[1]);\n\n  int wstatus;\n  do {\n    waitpid(debugged, &wstatus, WUNTRACED | WCONTINUED);\n  } while (!WIFSTOPPED(wstatus));\n\n  signal(SIGCHLD, SIG_IGN);\n  MASTER = true;\n  atexit(killChild);\n  return QBDIPRELOAD_NOT_HANDLED;\n}\n"
  },
  {
    "path": "tools/validator/master.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"master.h\"\n#include \"pipes.h\"\n#include \"validator.h\"\n#include \"validatorengine.h\"\n\n#include \"QBDI/Memory.hpp\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n\nvoid start_master(Process *debugged, pid_t instrumented, int ctrlfd, int datafd,\n                  int stdoutDbg, int stdoutDbi) {\n  const size_t BUFFER_SIZE = 128;\n  char mnemonic[BUFFER_SIZE], disassembly[BUFFER_SIZE];\n  char *env = nullptr;\n  int status;\n  FILE *ctrlPipe, *dataPipe;\n  QBDI::GPRState gprStateInstr, gprStateDbg;\n  QBDI::FPRState fprStateInstr, fprStateDbg;\n  QBDI::rword address;\n  EVENT event;\n  bool skipDebugger = false;\n  bool running = true;\n  int error = 0;\n\n  QBDI::setLogPriority(QBDI::LogPriority::ERROR);\n\n  // Create communication fifo\n  ctrlPipe = fdopen(ctrlfd, \"wb\");\n  dataPipe = fdopen(datafd, \"rb\");\n  if (ctrlPipe == nullptr || dataPipe == nullptr) {\n    QBDI_ERROR(\n        \"Could not open communication pipes with instrumented, exiting!\");\n    exit(VALIDATOR_ERR_PIPE_CREATION_FAIL);\n  }\n\n  // Handling verbosity\n  LogVerbosity verbosity = LogVerbosity::Stat;\n  if ((env = getenv(\"VALIDATOR_VERBOSITY\")) != nullptr) {\n    if (strcmp(env, \"Stat\") == 0)\n      verbosity = LogVerbosity::Stat;\n    else if (strcmp(env, \"Summary\") == 0)\n      verbosity = LogVerbosity::Summary;\n    else if (strcmp(env, \"Detail\") == 0)\n      verbosity = LogVerbosity::Detail;\n    else if (strcmp(env, \"Full\") == 0)\n      verbosity = LogVerbosity::Full;\n    else\n      QBDI_WARN(\"Did not understood VALIDATOR_VERBOSITY parameter: {}\\n\", env);\n  }\n\n  ValidatorEngine validator(debugged->getPID(), instrumented, stdoutDbg,\n                            stdoutDbi, verbosity);\n\n  running = true;\n  while (running) {\n    if (readEvent(&event, dataPipe) != 1) {\n      QBDI_ERROR(\"Lost the data pipe, exiting!\");\n      debugged->continueExecution();\n      error = VALIDATOR_ERR_DATA_PIPE_LOST;\n      break;\n    }\n    if (event == EVENT::EXIT) {\n      debugged->continueExecution();\n      running = false;\n      break;\n    } else if (event == EVENT::EXEC_TRANSFER) {\n      QBDI::rword transferAddress;\n      if (readExecTransferEvent(&transferAddress, dataPipe) != 1) {\n        QBDI_ERROR(\"Lost the data pipe, exiting!\");\n        debugged->continueExecution();\n        error = VALIDATOR_ERR_DATA_PIPE_LOST;\n        break;\n      }\n      validator.signalExecTransfer(transferAddress);\n    } else if (event == EVENT::INSTRUCTION) {\n      if (writeCommand(COMMAND::CONTINUE, ctrlPipe) != 1) {\n        QBDI_ERROR(\"Lost the control pipe, exiting!\");\n        debugged->continueExecution();\n        error = VALIDATOR_ERR_CTRL_PIPE_LOST;\n        break;\n      }\n      if (readInstructionEvent(&address, mnemonic, BUFFER_SIZE, disassembly,\n                               BUFFER_SIZE, &gprStateInstr, &fprStateInstr,\n                               &skipDebugger, dataPipe) != 1) {\n        QBDI_ERROR(\"Lost the data pipe, exiting!\");\n        debugged->continueExecution();\n        error = VALIDATOR_ERR_DATA_PIPE_LOST;\n        break;\n      }\n      if (not skipDebugger) {\n        debugged->setBreakpoint(\n            (void *)QBDI_GPR_GET(&gprStateInstr, QBDI::REG_PC));\n        do {\n          debugged->continueExecution();\n          status = debugged->waitForStatus();\n          if (hasExited(status)) {\n            QBDI_ERROR(\"Execution diverged, debugged process exited!\");\n            validator.signalCriticalState();\n            running = false;\n            writeCommand(COMMAND::STOP, ctrlPipe);\n            error = VALIDATOR_ERR_DBG_EXITED;\n            break;\n          } else if (hasCrashed(status)) {\n            QBDI_ERROR(\n                \"Something went really wrong, debugged process encoutered \"\n                \"signal \"\n                \"{}\",\n                WSTOPSIG(status));\n            validator.signalCriticalState();\n            running = false;\n            writeCommand(COMMAND::STOP, ctrlPipe);\n            error = VALIDATOR_ERR_DBG_CRASH;\n            break;\n          }\n          debugged->getProcessGPR(&gprStateDbg);\n          debugged->getProcessFPR(&fprStateDbg);\n        } while (QBDI_GPR_GET(&gprStateDbg, QBDI::REG_PC) !=\n                 QBDI_GPR_GET(&gprStateInstr, QBDI::REG_PC));\n      }\n      validator.signalNewState(address, mnemonic, disassembly, skipDebugger,\n                               &gprStateDbg, &fprStateDbg, &gprStateInstr,\n                               &fprStateInstr);\n      if (running) {\n        debugged->unsetBreakpoint();\n      }\n    } else if (event == EVENT::MISSMATCHMEMACCESS) {\n      bool doRead, mayRead, doWrite, mayWrite;\n      std::vector<QBDI::MemoryAccess> accesses;\n      if (readMismatchMemAccessEvent(&address, &doRead, &mayRead, &doWrite,\n                                     &mayWrite, accesses, dataPipe) != 1) {\n        QBDI_ERROR(\"Lost the data pipe, exiting!\");\n        debugged->continueExecution();\n        writeCommand(COMMAND::STOP, ctrlPipe);\n        error = VALIDATOR_ERR_DATA_PIPE_LOST;\n        break;\n      }\n      validator.signalAccessError(address, doRead, mayRead, doWrite, mayWrite,\n                                  accesses);\n    } else {\n      QBDI_ERROR(\"Unknown validator event {}\", event);\n      debugged->continueExecution();\n      error = VALIDATOR_ERR_UNEXPECTED_API_FAILURE;\n      break;\n    }\n  }\n\n  validator.flushLastLog();\n  validator.logCascades();\n  if ((env = getenv(\"VALIDATOR_COVERAGE\")) != nullptr) {\n    validator.logCoverage(env);\n  }\n\n  exit(error);\n}\n"
  },
  {
    "path": "tools/validator/master.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef MASTER_H\n#define MASTER_H\n\n#include <unistd.h>\n\n#include \"process.h\"\n\nvoid start_master(Process *debugged, pid_t instrumented, int ctrlfd, int datafd,\n                  int stdoutDbg, int stdoutDbi);\n\n#endif // MASTER_H\n"
  },
  {
    "path": "tools/validator/pipes.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"pipes.h\"\n#include <stdint.h>\n#include <string.h>\n\n// TODO: Proper error handling\n\nint readCString(char *str, size_t length, FILE *pipe) {\n  size_t i = 0;\n  while (i < length) {\n    if (fread((void *)&str[i], 1, 1, pipe) != 1) {\n      return 0;\n    }\n    if (str[i] == '\\0') {\n      break;\n    }\n    i += 1;\n  }\n  return 1;\n}\n\nint writeCString(const char *str, FILE *pipe) {\n  size_t len = strlen(str) + 1;\n  if (fwrite((void *)str, 1, len, pipe) != len) {\n    return 0;\n  }\n  return 1;\n}\n\nint readInstructionEvent(QBDI::rword *address, char *mnemonic,\n                         size_t mnemonic_len, char *disassembly,\n                         size_t disassembly_len, QBDI::GPRState *gprState,\n                         QBDI::FPRState *fprState, bool *debuggerSkip,\n                         FILE *pipe) {\n  if (fread((void *)address, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n  if (readCString(mnemonic, mnemonic_len, pipe) != 1) {\n    return 0;\n  }\n  if (readCString(disassembly, disassembly_len, pipe) != 1) {\n    return 0;\n  }\n  if (fread((void *)gprState, sizeof(QBDI::GPRState), 1, pipe) != 1) {\n    return 0;\n  }\n  if (fread((void *)fprState, sizeof(QBDI::FPRState), 1, pipe) != 1) {\n    return 0;\n  }\n  if (fread((void *)debuggerSkip, 1, 1, pipe) != 1) {\n    return 0;\n  }\n  return 1;\n}\n\nint writeInstructionEvent(QBDI::rword address, const char *mnemonic,\n                          const char *disassembly, QBDI::GPRState *gprState,\n                          QBDI::FPRState *fprState, bool debuggerSkip,\n                          FILE *pipe) {\n  if (writeEvent(EVENT::INSTRUCTION, pipe) != 1) {\n    return 0;\n  }\n  if (fwrite((void *)&address, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n  if (writeCString(mnemonic, pipe) != 1) {\n    return 0;\n  }\n  if (writeCString(disassembly, pipe) != 1) {\n    return 0;\n  }\n  if (fwrite((void *)gprState, sizeof(QBDI::GPRState), 1, pipe) != 1) {\n    return 0;\n  }\n  if (fwrite((void *)fprState, sizeof(QBDI::FPRState), 1, pipe) != 1) {\n    return 0;\n  }\n  if (fwrite((void *)&debuggerSkip, 1, 1, pipe) != 1) {\n    return 0;\n  }\n  fflush(pipe);\n  return 1;\n}\n\nint readMismatchMemAccessEvent(QBDI::rword *address, bool *doRead,\n                               bool *mayRead, bool *doWrite, bool *mayWrite,\n                               std::vector<QBDI::MemoryAccess> &accesses,\n                               FILE *pipe) {\n  if (fread((void *)address, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n  unsigned char flags;\n  if (fread((void *)&flags, sizeof(unsigned char), 1, pipe) != 1) {\n    return 0;\n  }\n  *doRead = flags & 0x8;\n  *mayRead = flags & 0x4;\n  *doWrite = flags & 0x2;\n  *mayWrite = flags & 0x1;\n\n  QBDI::rword accessSize;\n  if (fread((void *)&accessSize, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n  accesses.clear();\n  for (unsigned i = 0; i < accessSize; i++) {\n    QBDI::MemoryAccess access;\n    if (fread((void *)&access, sizeof(QBDI::MemoryAccess), 1, pipe) != 1) {\n      return 0;\n    }\n    accesses.push_back(access);\n  }\n  return 1;\n}\n\nint writeMismatchMemAccessEvent(QBDI::rword address, bool doRead, bool mayRead,\n                                bool doWrite, bool mayWrite,\n                                const std::vector<QBDI::MemoryAccess> &accesses,\n                                FILE *pipe) {\n  if (writeEvent(EVENT::MISSMATCHMEMACCESS, pipe) != 1) {\n    return 0;\n  }\n  if (fwrite((void *)&address, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n\n  unsigned char flags = 0;\n  flags |= doRead ? 0x8 : 0x0;\n  flags |= mayRead ? 0x4 : 0x0;\n  flags |= doWrite ? 0x2 : 0x0;\n  flags |= mayWrite ? 0x1 : 0x0;\n\n  if (fwrite((void *)&flags, sizeof(unsigned char), 1, pipe) != 1) {\n    return 0;\n  }\n\n  QBDI::rword accessSize = accesses.size();\n  if (fwrite((void *)&accessSize, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n  for (const auto &access : accesses) {\n    if (fwrite((const void *)&access, sizeof(QBDI::MemoryAccess), 1, pipe) !=\n        1) {\n      return 0;\n    }\n  }\n\n  fflush(pipe);\n  return 1;\n}\n\nint readExecTransferEvent(QBDI::rword *address, FILE *pipe) {\n  if (fread((void *)address, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n  return 1;\n}\n\nint writeExecTransferEvent(QBDI::rword address, FILE *pipe) {\n  if (writeEvent(EVENT::EXEC_TRANSFER, pipe) != 1) {\n    return 0;\n  }\n  if (fwrite((void *)&address, sizeof(QBDI::rword), 1, pipe) != 1) {\n    return 0;\n  }\n  fflush(pipe);\n  return 1;\n}\n\nint readEvent(EVENT *event, FILE *pipe) {\n  if (fread((void *)event, sizeof(EVENT), 1, pipe) != 1) {\n    return 0;\n  }\n  return 1;\n}\n\nint writeEvent(EVENT event, FILE *pipe) {\n  if (fwrite((void *)&event, sizeof(EVENT), 1, pipe) != 1) {\n    return 0;\n  }\n  fflush(pipe);\n  return 1;\n}\n\nint readCommand(COMMAND *command, FILE *pipe) {\n  if (fread((void *)command, sizeof(COMMAND), 1, pipe) != 1) {\n    return 0;\n  }\n  return 1;\n}\n\nint writeCommand(COMMAND command, FILE *pipe) {\n  if (fwrite((void *)&command, sizeof(COMMAND), 1, pipe) != 1) {\n    return 0;\n  }\n  fflush(pipe);\n  return 1;\n}\n"
  },
  {
    "path": "tools/validator/pipes.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef VALIDATOR_PIPES_H\n#define VALIDATOR_PIPES_H\n\n#include <memory.h>\n#include <stdio.h>\n#include <vector>\n\n#include <QBDI/Callback.h>\n#include <QBDI/State.h>\n\nenum EVENT {\n  INSTRUCTION,\n  MISSMATCHMEMACCESS,\n  EXEC_TRANSFER,\n  EXIT,\n};\n\ninline int format_as(EVENT e) { return e; }\n\nenum COMMAND {\n  CONTINUE,\n  STOP,\n};\n\nint readCString(char *str, size_t length, FILE *pipe);\n\nint writeCString(const char *str, FILE *pipe);\n\nint readInstructionEvent(QBDI::rword *address, char *mnemonic,\n                         size_t mnemonic_len, char *disassembly,\n                         size_t disassembly_len, QBDI::GPRState *gprState,\n                         QBDI::FPRState *fprState, bool *debuggerSkip,\n                         FILE *pipe);\n\nint writeInstructionEvent(QBDI::rword address, const char *mnemonic,\n                          const char *disassembly, QBDI::GPRState *gprState,\n                          QBDI::FPRState *fprState, bool debuggerSkip,\n                          FILE *pipe);\n\nint readMismatchMemAccessEvent(QBDI::rword *address, bool *doRead,\n                               bool *mayRead, bool *doWrite, bool *mayWrite,\n                               std::vector<QBDI::MemoryAccess> &accesses,\n                               FILE *pipe);\n\nint writeMismatchMemAccessEvent(QBDI::rword address, bool doRead, bool mayRead,\n                                bool doWrite, bool mayWrite,\n                                const std::vector<QBDI::MemoryAccess> &accesses,\n                                FILE *pipe);\n\nint readExecTransferEvent(QBDI::rword *address, FILE *pipe);\n\nint writeExecTransferEvent(QBDI::rword address, FILE *pipe);\n\nint readEvent(EVENT *event, FILE *pipe);\n\nint writeEvent(EVENT event, FILE *pipe);\n\nint readCommand(COMMAND *command, FILE *pipe);\n\nint writeCommand(COMMAND command, FILE *pipe);\n\n#endif // VALIDATOR_PIPES_H\n"
  },
  {
    "path": "tools/validator/process.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef PROCESS_H\n#define PROCESS_H\n\n#include <sys/types.h>\n\n#include \"QBDI/State.h\"\n\nclass Process {\n\npublic:\n  virtual ~Process() = default;\n\n  virtual pid_t getPID() = 0;\n\n  virtual void setBreakpoint(void *address) = 0;\n\n  virtual void unsetBreakpoint() = 0;\n\n  virtual void continueExecution() = 0;\n\n  virtual int waitForStatus() = 0;\n\n  virtual void getProcessGPR(QBDI::GPRState *gprState) = 0;\n\n  virtual void getProcessFPR(QBDI::FPRState *fprState) = 0;\n};\n\nbool hasExited(int status);\n\nbool hasStopped(int status);\n\nbool hasCrashed(int status);\n\n#endif // PROCESS_H\n"
  },
  {
    "path": "tools/validator/validator.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef VALIDATOR_H\n#define VALIDATOR_H\n\n#define VALIDATOR_ERR_NO_ERROR 0\n#define VALIDATOR_ERR_PIPE_CREATION_FAIL 1\n#define VALIDATOR_ERR_DATA_PIPE_LOST 2\n#define VALIDATOR_ERR_CTRL_PIPE_LOST 3\n#define VALIDATOR_ERR_DBG_EXITED 4\n#define VALIDATOR_ERR_DBG_CRASH 5\n#define VALIDATOR_ERR_UNEXPECTED_API_FAILURE 6\n\n#endif // VALIDATOR_H\n"
  },
  {
    "path": "tools/validator/validatorengine.cpp",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"validatorengine.h\"\n#include <algorithm>\n#include <cstring>\n#include <inttypes.h>\n#include <poll.h>\n#include <unistd.h>\n\nstd::pair<QBDI::rword, QBDI::rword> getValidOffsetRange(QBDI::rword address,\n                                                        pid_t pid) {\n  std::vector<QBDI::MemoryMap> maps = QBDI::getRemoteProcessMaps(pid);\n  for (QBDI::MemoryMap &m : maps) {\n    if (m.range.contains(address)) {\n      return std::make_pair(address - m.range.start(),\n                            m.range.end() - address - 1);\n    }\n  }\n  return std::make_pair(0, 0);\n}\n\nssize_t ValidatorEngine::logEntryLookup(uint64_t execID) {\n  size_t upper = savedLogs.size();\n  size_t lower = 0;\n  while (upper != lower) {\n    size_t i = (upper + lower) / 2;\n    if (savedLogs[i]->execID < execID) {\n      lower = i;\n    } else if (savedLogs[i]->execID > execID) {\n      upper = i;\n    } else {\n      return (ssize_t)i;\n    }\n  }\n  return -1;\n}\n\nQBDI::MemoryMap *ValidatorEngine::getModule(QBDI::rword address) {\n  static QBDI::MemoryMap *module = nullptr;\n  static std::vector<QBDI::MemoryMap> memoryModule;\n\n  // try to find module name of current address\n  if (module != nullptr) {\n    if (!module->range.contains(address)) {\n      module = nullptr;\n    }\n  }\n\n  if (module == nullptr) {\n    for (auto &m : memoryModule) {\n      if (m.range.contains(address)) {\n        module = &m;\n        break;\n      }\n    }\n  }\n\n  if (module == nullptr) {\n    memoryModule.clear();\n    for (auto &m : QBDI::getRemoteProcessMaps(instrumented)) {\n      if (m.permission & QBDI::PF_EXEC) {\n        memoryModule.push_back(m);\n      }\n    }\n    for (auto &m : memoryModule) {\n      if (m.range.contains(address)) {\n        module = &m;\n        break;\n      }\n    }\n  }\n\n  return module;\n}\n\nvoid ValidatorEngine::outputLogEntry(const LogEntry &entry,\n                                     bool showMemoryError, bool showDiffError) {\n  QBDI::MemoryMap *module = getModule(entry.address);\n\n  fprintf(stderr, \"ExecID: %\" PRIu64 \" \\t%25s 0x%016\" PRIRWORD \": %s\\n\",\n          entry.execID, (module != nullptr) ? module->name.c_str() : \"\",\n          entry.address, entry.disassembly.c_str());\n  if (entry.transfer != 0) {\n    QBDI::MemoryMap *transfer_module = getModule(entry.transfer);\n    fprintf(stderr, \"\\tCaused a transfer to address 0x%\" PRIRWORD \" %s\\n\",\n            entry.transfer,\n            (transfer_module != nullptr) ? transfer_module->name.c_str() : \"\");\n  }\n  if (entry.accessError.get() != nullptr && showMemoryError) {\n    const AccessError &access = *entry.accessError;\n    fprintf(stderr, \"\\tMemoryAccess Error (mnemonic : %s):\\n\",\n            entry.mnemonic.c_str());\n    if (access.doRead and !access.mayRead) {\n      fprintf(stderr, \"\\t\\t- Found unexpected read\\n\");\n    } else if (!access.doRead and access.mayRead) {\n      fprintf(stderr, \"\\t\\t- Missing read\\n\");\n    }\n    if (access.doWrite and !access.mayWrite) {\n      fprintf(stderr, \"\\t\\t- Found unexpected write\\n\");\n    } else if (!access.doWrite and access.mayWrite) {\n      fprintf(stderr, \"\\t\\t- Missing write\\n\");\n    }\n    for (unsigned i = 0; i < access.accesses.size(); i++) {\n      const QBDI::MemoryAccess &a = access.accesses[i];\n      fprintf(stderr,\n              \"\\t\\t[%d] type=%s%s, addr=0x%\" PRIRWORD\n              \", size=0x%x, value=0x%\" PRIRWORD \"\\n\",\n              i, (a.type & QBDI::MemoryAccessType::MEMORY_READ) ? \"r\" : \"\",\n              (a.type & QBDI::MemoryAccessType::MEMORY_WRITE) ? \"w\" : \"\",\n              a.accessAddress, a.size, a.value);\n    }\n  }\n  if (showDiffError) {\n    for (ssize_t eID : entry.errorIDs) {\n      if (errors[eID].severity == ErrorSeverity::NoImpact) {\n        fprintf(stderr, \"\\tError with no impact \");\n      } else if (errors[eID].severity == ErrorSeverity::NonCritical) {\n        fprintf(stderr, \"\\tError with non critical impact \");\n      } else if (errors[eID].severity == ErrorSeverity::Critical) {\n        fprintf(stderr, \"\\tError with critical impact \");\n      }\n      fprintf(stderr,\n              \"on %s: 0x%\" PRIRWORD \" (real) != 0x%\" PRIRWORD \" (qbdi)\\n\",\n              errors[eID].regName, errors[eID].real, errors[eID].qbdi);\n    }\n  }\n}\n\nssize_t ValidatorEngine::diff(const char *regName, QBDI::rword real,\n                              QBDI::rword qbdi) {\n  // No diff, no error\n  if (real == qbdi) {\n    return -1;\n  }\n\n  DiffError e;\n  e.regName = regName;\n  e.real = (QBDI::rword)real;\n  e.qbdi = (QBDI::rword)qbdi;\n  e.severity = ErrorSeverity::NoImpact;\n  e.causeExecID = execID;\n  e.cascadeID = execID;\n\n  // If lastLogEntry has at least one error, consider this new error to be a\n  // cascade\n  if (lastLogEntry != nullptr && lastLogEntry->errorIDs.size() > 0) {\n    e.cascadeID = errors[lastLogEntry->errorIDs[0]].cascadeID;\n    // Upgrade previous error severity to non critical because they caused a\n    // cascade\n    for (ssize_t eID : lastLogEntry->errorIDs) {\n      errors[eID].severity = ErrorSeverity::NonCritical;\n    }\n  } else {\n    // If equivalent error exist, consider this new error to be a cascade\n    for (DiffError &prevError : errors) {\n      if (e.real == prevError.real && e.qbdi == prevError.qbdi) {\n        e.cascadeID = prevError.cascadeID;\n        // Upgrade previous error severity to non critical because they caused a\n        // cascade\n        prevError.severity = ErrorSeverity::NonCritical;\n        break;\n      }\n    }\n  }\n  // else new cascadeID\n  errors.push_back(e);\n  return errors.size() - 1;\n}\n\nssize_t ValidatorEngine::diffGPR(unsigned regID, QBDI::rword real,\n                                 QBDI::rword qbdi) {\n  // No diff, no error\n  if (real == qbdi) {\n    return -1;\n  }\n\n  // First try to explain the diff using the diffmaps\n  for (const DiffMap &d : diffMaps) {\n    if (real - d.real == qbdi - d.qbdi &&\n        ((d.real >= real && d.real - real <= d.lowerOffset) ||\n         (real >= d.real && real - d.real <= d.upperOffset))) {\n      // Explained, no error\n      return -1;\n    }\n  }\n\n  // If this error is just propagating from the previous state, just copy the\n  // pointer\n  if (lastLogEntry != nullptr) {\n    for (ssize_t eID : lastLogEntry->errorIDs) {\n      if (strcmp(QBDI::GPR_NAMES[regID], errors[eID].regName) == 0 &&\n          real == errors[eID].real && qbdi == errors[eID].qbdi) {\n        return eID;\n      }\n    }\n  }\n\n  // New error at the begining of execution or after an ExecTransfer, add to\n  // diffmaps\n  if (lastLogEntry == nullptr || curLogEntry->transfer != 0) {\n    DiffMap d;\n    d.real = real;\n    d.qbdi = qbdi;\n    std::pair<QBDI::rword, QBDI::rword> rangeDbg =\n        getValidOffsetRange(real, debugged);\n    std::pair<QBDI::rword, QBDI::rword> rangeInstr =\n        getValidOffsetRange(qbdi, instrumented);\n    d.lowerOffset = std::min(rangeDbg.first, rangeInstr.first);\n    d.upperOffset = std::min(rangeDbg.second, rangeInstr.second);\n    d.causeExecID = execID;\n    diffMaps.push_back(d);\n    return -1;\n  }\n\n  // Else normal error diff\n  return diff(QBDI::GPR_NAMES[regID], real, qbdi);\n}\n\nssize_t ValidatorEngine::diffSPR(const char *regName, QBDI::rword real,\n                                 QBDI::rword qbdi) {\n  // No diff, no error\n  if (real == qbdi) {\n    return -1;\n  }\n\n  // First try to explain the diff using the exact diff of the diffmaps\n  for (const DiffMap &d : diffMaps) {\n    if (d.lowerOffset == 0 && d.upperOffset == 0 && real == d.real &&\n        qbdi == d.qbdi) {\n      return -1;\n    }\n  }\n\n  // If this error is just propagating from the previous state, just copy the\n  // pointer\n  if (lastLogEntry != nullptr) {\n    for (ssize_t eID : lastLogEntry->errorIDs) {\n      if (strcmp(regName, errors[eID].regName) == 0 &&\n          real == errors[eID].real && qbdi == errors[eID].qbdi) {\n        return eID;\n      }\n    }\n  }\n\n  // New error at the begining of execution or after an ExecTransfer, add to\n  // diffmaps as an exact diff\n  if (lastLogEntry == nullptr || curLogEntry->transfer != 0) {\n    DiffMap d;\n    d.real = real;\n    d.qbdi = qbdi;\n    d.lowerOffset = 0;\n    d.upperOffset = 0;\n    d.causeExecID = execID;\n    diffMaps.push_back(d);\n    return -1;\n  }\n\n  // Else normal error diff\n  return diff(regName, real, qbdi);\n}\n\nvoid ValidatorEngine::signalNewState(QBDI::rword address, const char *mnemonic,\n                                     const char *disassembly, bool skipDebugger,\n                                     const QBDI::GPRState *gprStateDbg,\n                                     const QBDI::FPRState *fprStateDbg,\n                                     const QBDI::GPRState *gprStateInstr,\n                                     const QBDI::FPRState *fprStateInstr) {\n\n  if (curLogEntry != nullptr) {\n    if (not skipDebugger) {\n      compareState(gprStateDbg, fprStateDbg, gprStateInstr, fprStateInstr);\n    }\n    // If this logEntry generated at least one new error, saved it\n    if (!curLogEntry->saved) {\n      for (const ssize_t eID : curLogEntry->errorIDs) {\n        if (errors[eID].causeExecID == execID) {\n          curLogEntry->saved = true;\n          break;\n        }\n      }\n      if (curLogEntry->accessError.get() != nullptr) {\n        curLogEntry->saved = true;\n      }\n      if (curLogEntry->saved) {\n        savedLogs.push_back(std::unique_ptr<LogEntry>(curLogEntry));\n      }\n    }\n  }\n\n  if (lastLogEntry != nullptr) {\n    if (verbosity == LogVerbosity::Full) {\n      outputLogEntry(*lastLogEntry);\n    }\n    if (!lastLogEntry->saved) {\n      delete lastLogEntry;\n    }\n  }\n  lastLogEntry = curLogEntry;\n\n  execID++;\n\n  coverage[std::string(mnemonic)]++;\n  curLogEntry = new LogEntry(execID, address, disassembly, mnemonic);\n}\n\nvoid ValidatorEngine::signalAccessError(\n    QBDI::rword address, bool doRead, bool mayRead, bool doWrite, bool mayWrite,\n    const std::vector<QBDI::MemoryAccess> &accesses) {\n  if (curLogEntry != nullptr && curLogEntry->accessError.get() == nullptr &&\n      curLogEntry->address == address) {\n    accessError += 1;\n    curLogEntry->accessError = std::make_unique<AccessError>(\n        doRead, mayRead, doWrite, mayWrite, accesses);\n    memAccessMnemonicSet.insert(curLogEntry->mnemonic);\n  }\n}\n\nvoid ValidatorEngine::signalExecTransfer(QBDI::rword address) {\n  if (curLogEntry != nullptr) {\n    curLogEntry->transfer = address;\n  }\n}\n\nvoid ValidatorEngine::signalCriticalState() {\n  // Mark current errors as critical\n  if (lastLogEntry != nullptr) {\n    for (const ssize_t eID : lastLogEntry->errorIDs) {\n      errors[eID].severity = ErrorSeverity::Critical;\n    }\n  }\n}\n\nvoid ValidatorEngine::flushLastLog() {\n  if (lastLogEntry != nullptr) {\n    if (verbosity == LogVerbosity::Full) {\n      outputLogEntry(*lastLogEntry);\n    }\n    if (!lastLogEntry->saved) {\n      delete lastLogEntry;\n    }\n    lastLogEntry = nullptr;\n  }\n  if (curLogEntry != nullptr) {\n    if (verbosity == LogVerbosity::Full) {\n      outputLogEntry(*curLogEntry);\n    }\n    if (!curLogEntry->saved) {\n      delete lastLogEntry;\n    }\n    curLogEntry = nullptr;\n  }\n  syncCompareThread();\n}\n\nvoid ValidatorEngine::logCascades() {\n  std::vector<Cascade> cascades;\n  if (verbosity >= LogVerbosity::Stat) {\n    size_t noImpactCount = 0;\n    size_t nonCriticalCount = 0;\n    size_t criticalCount = 0;\n    fprintf(stderr, \"Stats\\n\");\n    fprintf(stderr, \"=====\\n\\n\");\n    fprintf(stderr, \"Executed %\" PRIu64 \" total instructions\\n\", execID);\n    fprintf(stderr, \"Executed %zu unique instructions\\n\", coverage.size());\n    fprintf(stderr, \"Encountered %zu difference mappings\\n\", diffMaps.size());\n    fprintf(stderr, \"Encountered %\" PRIu64 \" memoryAccess errors\\n\",\n            accessError);\n    fprintf(stderr, \"Encountered %zu memoryAccess unique errors\\n\",\n            memAccessMnemonicSet.size());\n    fprintf(stderr, \"SizeOutput: %zu %zu\\n\", outputDbg.size(),\n            outputDbi.size());\n    if (outputDbg.size() == outputDbi.size() and\n        memcmp(outputDbg.data(), outputDbi.data(), outputDbi.size()) == 0) {\n      fprintf(stderr, \"SameOutput: True\\n\");\n    } else {\n      fprintf(stderr, \"SameOutput: False\\n\");\n    }\n    fprintf(stderr, \"Encountered %zu errors:\\n\", errors.size());\n    // Compute error stats, assemble cascades\n    for (const DiffError &error : errors) {\n      bool found = false;\n      for (size_t i = 0; i < cascades.size(); i++) {\n        if (cascades[i].cascadeID == error.cascadeID) {\n          found = true;\n          cascades[i].execIDs.push_back(error.causeExecID);\n          if (error.severity > cascades[i].severity) {\n            cascades[i].severity = error.severity;\n          }\n        }\n      }\n      if (!found) {\n        cascades.push_back(Cascade{\n            error.cascadeID,\n            savedLogs[logEntryLookup(error.causeExecID)]->address,\n            error.severity, std::vector<uint64_t>(), std::vector<uint64_t>()});\n        cascades.back().execIDs.push_back(error.causeExecID);\n      }\n\n      if (error.severity == ErrorSeverity::NoImpact)\n        noImpactCount++;\n      else if (error.severity == ErrorSeverity::NonCritical)\n        nonCriticalCount++;\n      else if (error.severity == ErrorSeverity::Critical)\n        criticalCount++;\n    }\n    fprintf(stderr, \"\\tNo impact errors: %zu\\n\", noImpactCount);\n    fprintf(stderr, \"\\tNon critical errors: %zu\\n\", nonCriticalCount);\n    fprintf(stderr, \"\\tCritical errors: %zu\\n\", criticalCount);\n    fprintf(stderr, \"Encountered %zu error cascades:\\n\", cascades.size());\n    // Compute cascade stats\n    noImpactCount = 0;\n    nonCriticalCount = 0;\n    criticalCount = 0;\n    for (size_t i = 0; i < cascades.size(); i++) {\n      if (cascades[i].severity == ErrorSeverity::NoImpact)\n        noImpactCount++;\n      else if (cascades[i].severity == ErrorSeverity::NonCritical)\n        nonCriticalCount++;\n      else if (cascades[i].severity == ErrorSeverity::Critical)\n        criticalCount++;\n    }\n    fprintf(stderr, \"\\tNo impact cascades: %zu\\n\", noImpactCount);\n    fprintf(stderr, \"\\tNon critical cascades: %zu\\n\", nonCriticalCount);\n    fprintf(stderr, \"\\tCritical cascades: %zu\\n\", criticalCount);\n  }\n  if (verbosity >= LogVerbosity::Summary) {\n    fprintf(stderr, \"\\n\\n\");\n    fprintf(stderr, \"Error MemoryAccess:\\n\");\n    fprintf(stderr, \"==============\\n\\n\");\n    for (auto &l : savedLogs) {\n      if (l->accessError.get() != nullptr) {\n        outputLogEntry(*l, true, false);\n      }\n    }\n    fprintf(stderr, \"\\n\\n\");\n    fprintf(stderr, \"Error cascades:\\n\");\n    fprintf(stderr, \"==============\\n\\n\");\n    // Simplify, remove duplicates and sort cascades\n    for (size_t i = 0; i < cascades.size();) {\n      size_t eidx = 0;\n\n      while (eidx < cascades[i].execIDs.size() - 1) {\n        if (cascades[i].execIDs[eidx] == cascades[i].execIDs[eidx + 1]) {\n          cascades[i].execIDs.erase(cascades[i].execIDs.begin() + eidx + 1);\n        } else {\n          eidx++;\n        }\n      }\n\n      bool sameCascade = false;\n      for (size_t j = 0; j < i; j++) {\n        // Check for duplicate\n        if (cascades[j].causeAddress == cascades[i].causeAddress &&\n            cascades[j].severity == cascades[i].severity) {\n\n          /* More strict similarity test, disabled because too strict\n         cascades[j].execIDs.size() == cascades[i].execIDs.size()) {\n\n          for(size_t k = 0; k < cascades[i].execIDs.size(); k++) {\n              if(logEntryLookup(cascades[j].execIDs[k])->address !=\n                 logEntryLookup(cascades[i].execIDs[k])->address) {\n                  sameCascade = false;\n                  break;\n              }\n          }*/\n          sameCascade = true;\n        }\n\n        // Remove if duplicate\n        if (sameCascade) {\n          cascades[j].similarCascade.push_back(cascades[i].cascadeID);\n          cascades[i] = cascades.back();\n          cascades.pop_back();\n          break;\n        }\n        // Sort\n        else {\n          if (cascades[i].severity > cascades[j].severity) {\n            Cascade tmp = cascades[j];\n            cascades[j] = cascades[i];\n            cascades[i] = tmp;\n          }\n        }\n      }\n      // Conditionnal increment if we didn't suppress cascade i as duplicate\n      if (sameCascade == false) {\n        i++;\n      }\n    }\n    for (size_t i = 0; i < cascades.size(); i++) {\n      fprintf(stderr, \"Cascade %\" PRIu64 \":\\n\", cascades[i].cascadeID);\n      fprintf(stderr, \"--------------------\\n\\n\");\n      fprintf(stderr, \"%zu other similar cascade encountered\\n\",\n              cascades[i].similarCascade.size());\n      fprintf(stderr, \"Cascade length: %zu\\n\", cascades[i].execIDs.size());\n\n      if (cascades[i].severity == ErrorSeverity::NoImpact)\n        fprintf(stderr, \"No Impact classification\\n\");\n      else if (cascades[i].severity == ErrorSeverity::NonCritical)\n        fprintf(stderr, \"Non Critical Impact classification\\n\");\n      else if (cascades[i].severity == ErrorSeverity::Critical)\n        fprintf(stderr, \"Critical Impact classification\\n\");\n\n      if (verbosity == LogVerbosity::Summary) {\n        fprintf(stderr, \"Cause:\\n\");\n        outputLogEntry(*savedLogs[logEntryLookup(cascades[i].cascadeID)].get(),\n                       false);\n      } else if (verbosity >= LogVerbosity::Detail) {\n        fprintf(stderr, \"Chain:\\n\");\n        for (ssize_t eID : cascades[i].execIDs) {\n          outputLogEntry(*savedLogs[logEntryLookup(eID)].get(), false);\n        }\n      }\n      fprintf(stderr, \"\\n\\n\");\n    }\n  }\n}\n\nvoid ValidatorEngine::logCoverage(const char *filename) {\n  FILE *coverageFile = fopen(filename, \"w\");\n  std::vector<std::pair<std::string, uint64_t>> coverageList;\n\n  for (const auto &c : coverage) {\n    coverageList.push_back(std::make_pair(c.first, c.second));\n  }\n  std::sort(coverageList.begin(), coverageList.end(),\n            KVComp<std::string, uint64_t>);\n  for (const std::pair<std::string, uint64_t> &c : coverageList) {\n    fprintf(coverageFile, \"%s: %\" PRIu64 \"\\n\", c.first.c_str(), c.second);\n  }\n\n  fclose(coverageFile);\n}\n\nvoid ValidatorEngine::startCompareThread() {\n  threadStop = false;\n  outputCMPThread = std::thread(&ValidatorEngine::outputCompareThread, this);\n}\n\nvoid ValidatorEngine::outputCompareThread() {\n  uint8_t buffer[4096];\n  outputDbg.clear();\n  outputDbi.clear();\n\n  int retryBeforeExit = 2;\n\n  while (retryBeforeExit > 0) {\n    struct pollfd fds[2];\n\n    fds[0].fd = stdoutDbg;\n    fds[1].fd = stdoutDbi;\n    fds[0].events = POLLIN | POLLRDBAND;\n    fds[1].events = POLLIN | POLLRDBAND;\n\n    int ret = poll(fds, 2, 100);\n\n    if (ret < 0) {\n      perror(\"select()\");\n      break;\n    }\n\n    bool hasRead = false;\n\n    if (fds[0].revents & (POLLIN | POLLRDBAND)) {\n      size_t readLen = read(stdoutDbg, buffer, sizeof(buffer));\n      outputDbg.insert(outputDbg.end(), buffer, buffer + readLen);\n      hasRead = true;\n    }\n\n    if (fds[1].revents & (POLLIN | POLLRDBAND)) {\n      size_t readLen = read(stdoutDbi, buffer, sizeof(buffer));\n      outputDbi.insert(outputDbi.end(), buffer, buffer + readLen);\n      hasRead = true;\n    }\n\n    if (threadStop and not hasRead) {\n      retryBeforeExit -= 1;\n    }\n  }\n}\n\nvoid ValidatorEngine::syncCompareThread() {\n  threadStop = true;\n  outputCMPThread.join();\n}\n"
  },
  {
    "path": "tools/validator/validatorengine.h",
    "content": "/*\n * This file is part of QBDI.\n *\n * Copyright 2017 - 2025 Quarkslab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef VALIDATORENGINE_H\n#define VALIDATORENGINE_H\n\n#include <map>\n#include <memory>\n#include <set>\n#include <thread>\n#include <utility>\n#include <vector>\n\n#include <QBDI/Callback.h>\n#include <QBDI/Memory.hpp>\n#include \"Utility/LogSys.h\"\n\nenum LogVerbosity { Stat, Summary, Detail, Full };\n\nenum ErrorSeverity {\n  NoImpact,    // No crash, no cascade\n  NonCritical, // No crash but cascade\n  Critical     // crash\n};\n\nstruct DiffError {\n  const char *regName;\n  QBDI::rword real;\n  QBDI::rword qbdi;\n  ErrorSeverity severity;\n  uint64_t cascadeID;\n  uint64_t causeExecID;\n};\n\nstruct DiffMap {\n  QBDI::rword real;\n  QBDI::rword qbdi;\n  QBDI::rword lowerOffset;\n  QBDI::rword upperOffset;\n  uint64_t causeExecID;\n};\n\nstruct Cascade {\n  uint64_t cascadeID;\n  QBDI::rword causeAddress;\n  ErrorSeverity severity;\n  std::vector<uint64_t> execIDs;\n  std::vector<uint64_t> similarCascade;\n};\n\nstruct AccessError {\n  bool doRead;\n  bool mayRead;\n  bool doWrite;\n  bool mayWrite;\n  std::vector<QBDI::MemoryAccess> accesses;\n\n  AccessError(bool doRead, bool mayRead, bool doWrite, bool mayWrite,\n              const std::vector<QBDI::MemoryAccess> &accesses)\n      : doRead(doRead), mayRead(mayRead), doWrite(doWrite), mayWrite(mayWrite),\n        accesses(accesses) {}\n};\n\nclass LogEntry {\n\npublic:\n  uint64_t execID;\n  QBDI::rword address;\n  std::string disassembly;\n  std::string mnemonic;\n  QBDI::rword transfer;\n  bool saved;\n  std::unique_ptr<AccessError> accessError;\n\n  std::vector<ssize_t> errorIDs;\n\n  LogEntry(uint64_t execID, QBDI::rword address, const char *disassembly,\n           const char *mnemonic)\n      : execID(execID), address(address), disassembly(disassembly),\n        mnemonic(mnemonic), transfer(0), saved(false) {}\n};\n\ntemplate <typename T1, typename T2>\nbool KVComp(const std::pair<T1, T2> &a, const std::pair<T1, T2> &b) {\n  return a.second > b.second;\n}\n\nclass ValidatorEngine {\n  LogEntry *lastLogEntry;\n  LogEntry *curLogEntry;\n  std::vector<DiffMap> diffMaps;\n  std::vector<std::unique_ptr<LogEntry>> savedLogs;\n  std::vector<DiffError> errors;\n  std::map<std::string, uint64_t> coverage;\n  std::set<std::string> memAccessMnemonicSet;\n\n  QBDI::rword debugged;\n  QBDI::rword instrumented;\n  LogVerbosity verbosity;\n  uint64_t execID;\n  uint64_t accessError;\n\n  // Shared variable of the Thread\n  std::thread outputCMPThread;\n  bool threadStop;\n  int stdoutDbg;\n  int stdoutDbi;\n  std::vector<uint8_t> outputDbg;\n  std::vector<uint8_t> outputDbi;\n\n  ssize_t logEntryLookup(uint64_t execID);\n\n  QBDI::MemoryMap *getModule(QBDI::rword address);\n\n  void outputLogEntry(const LogEntry &entry, bool showMemoryError = true,\n                      bool showDiffError = true);\n\n  std::vector<std::pair<QBDI::rword, QBDI::rword>> getMapsFromPID(pid_t pid);\n\n  ssize_t diff(const char *regName, QBDI::rword real, QBDI::rword qbdi);\n\n  ssize_t diffGPR(unsigned regID, QBDI::rword real, QBDI::rword qbdi);\n\n  ssize_t diffSPR(const char *regName, QBDI::rword real, QBDI::rword qbdi);\n\n  void compareState(const QBDI::GPRState *gprStateDbg,\n                    const QBDI::FPRState *fprStateDbg,\n                    const QBDI::GPRState *gprStateInstr,\n                    const QBDI::FPRState *fprStateInstr);\n\n  void startCompareThread();\n  void outputCompareThread();\n  void syncCompareThread();\n\npublic:\n  ValidatorEngine(pid_t debugged, pid_t instrumented, int stdoutDbg,\n                  int stdoutDbi, LogVerbosity verbosity)\n      : lastLogEntry(nullptr), curLogEntry(nullptr), debugged(debugged),\n        instrumented(instrumented), verbosity(verbosity), execID(0),\n        accessError(0), threadStop(false), stdoutDbg(stdoutDbg),\n        stdoutDbi(stdoutDbi) {\n    startCompareThread();\n  }\n\n  ValidatorEngine(const ValidatorEngine &o) = delete;\n  ValidatorEngine(ValidatorEngine &&o) = delete;\n\n  ValidatorEngine &operator=(const ValidatorEngine &other) = delete;\n  ValidatorEngine &operator=(ValidatorEngine &&other) = delete;\n\n  void signalNewState(QBDI::rword address, const char *mnemonic,\n                      const char *disassembly, bool skipDebugger,\n                      const QBDI::GPRState *gprStateDbg,\n                      const QBDI::FPRState *fprStateDbg,\n                      const QBDI::GPRState *gprStateInstr,\n                      const QBDI::FPRState *fprStateInstr);\n\n  void signalAccessError(QBDI::rword address, bool doRead, bool mayRead,\n                         bool doWrite, bool mayWrite,\n                         const std::vector<QBDI::MemoryAccess> &accesses);\n\n  void signalExecTransfer(QBDI::rword address);\n\n  void signalCriticalState();\n\n  void flushLastLog();\n\n  void logCascades();\n\n  void logCoverage(const char *filename);\n};\n\n#endif // VALIDATORENGINE_H\n"
  }
]