Full Code of frida/frida for AI

main 6d77ff009fb3 cached
31 files
144.1 KB
40.3k tokens
17 symbols
1 requests
Download .txt
Repository: frida/frida
Branch: main
Commit: 6d77ff009fb3
Files: 31
Total size: 144.1 KB

Directory structure:
gitextract_t62922vv/

├── .cirrus.yml
├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── actions/
│   │   ├── package-artifact-file/
│   │   │   └── action.yml
│   │   ├── package-artifact-files-as-sfx/
│   │   │   └── action.yml
│   │   ├── package-artifact-files-as-tarball/
│   │   │   └── action.yml
│   │   ├── package-ios-assets/
│   │   │   └── action.yml
│   │   ├── package-tvos-assets/
│   │   │   └── action.yml
│   │   ├── prepare-python-packages/
│   │   │   └── action.yml
│   │   ├── publish-debs/
│   │   │   └── action.yml
│   │   ├── setup-freebsd-env/
│   │   │   └── action.yml
│   │   ├── setup-linux-env/
│   │   │   └── action.yml
│   │   ├── setup-macos-env/
│   │   │   └── action.yml
│   │   └── setup-windows-env/
│   │       └── action.yml
│   ├── scripts/
│   │   ├── package-cirrus-ci-artifacts.sh
│   │   ├── rename-release-assets.sh
│   │   └── repo.py
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .gitmodules
├── BSDmakefile
├── CONTRIBUTING.md
├── COPYING
├── Makefile
├── README.md
├── configure
├── configure.bat
├── make.bat
├── meson.build
├── meson.options
└── tools/
    └── ensure-submodules.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .cirrus.yml
================================================
task:
  name: freebsd-x86_64
  freebsd_instance:
    image_family: freebsd-15-0-amd64-ufs
  environment:
    S3_ACCESS_KEY: ENCRYPTED[2f368bef589e43779394e459191eaad2fc4cc50dc36c6f03168a1e8e43897476e8bf596fd4dd0d988bcbab8fa8e171f8]
    S3_SECRET_KEY: ENCRYPTED[45f352473433a1053622a662ce8d57bc7421bce5772c9c5caf4a0e54f63944bd420f71101e014876e038f0c66c134ac2]
    CF_EMAIL: ENCRYPTED[a9f0dcd0ee83fa111c36f092f34b3d5660acafe0d07804d6b6a89bfa70a127ef8305f21089408959c51e23ecd2fd3622]
    CF_TOKEN: ENCRYPTED[4f9f3f7c8603fd32978b825d65f34b06fce8e51bedb4303744c799d21fc38266173a6be5a7a46b5e616573d27c9f6e91]
    PYTHON: /usr/local/bin/python3.11
    PYTHON3: /usr/local/bin/python3.11
  timeout_in: 2h
  install_dependencies_script: |
    pkg install -y \
      bash \
      bison \
      git \
      go \
      npm \
      py311-pip \
      py311-setuptools
    echo "CIRRUS_SHELL=$(which bash)" >> $CIRRUS_ENV
    pip install awscli typing-extensions
    npm install -g cloudflare-cli@5.0.5
  clone_script: |
    if [ -z "$CIRRUS_PR" ]; then
      git clone --branch=$CIRRUS_BRANCH https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR
    else
      git clone https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR
      git fetch origin pull/$CIRRUS_PR/head:pull/$CIRRUS_PR
    fi
    git reset --hard $CIRRUS_CHANGE_IN_REPO
    $PYTHON tools/ensure-submodules.py
  setup_environment_script: |
    echo "FRIDA_PREFIX=$CIRRUS_WORKING_DIR/dist" >> $CIRRUS_ENV
    echo "FRIDA_VERSION=$(python releng/frida_version.py)" >> $CIRRUS_ENV
    mkdir -p ~/.aws
    (
      echo "[default]"
      echo "region = us-east-1"
    ) > ~/.aws/config
    (
      echo "[default]"
      echo "aws_access_key_id = $S3_ACCESS_KEY"
      echo "aws_secret_access_key = $S3_SECRET_KEY"
    ) > ~/.aws/credentials
    (
      echo "defaults:"
      echo "    domain: frida.re"
      echo "    email: $CF_EMAIL"
      echo "    token: $CF_TOKEN"
    ) > ~/.cfcli.yml
  roll_toolchain_script: $PYTHON releng/deps.py roll toolchain freebsd-x86_64 --activate
  roll_sdk_script: $PYTHON releng/deps.py roll sdk freebsd-x86_64
  configure_script: >-
    ./configure
    "--prefix=$FRIDA_PREFIX"
    --enable-gadget
    --enable-server
    --enable-portal
    --enable-inject
    --disable-frida-tools
    --
    -Dfrida-gum:devkits=gum,gumjs
    -Dfrida-core:compiler_backend=enabled
    -Dfrida-core:devkits=core
  compile_script: make
  install_script: make install
  frida_gum_devkit_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/lib/frida/devkits/gum/*"
  frida_gumjs_devkit_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/lib/frida/devkits/gumjs/*"
  frida_core_devkit_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/lib/frida/devkits/core/*"
  frida_server_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/bin/frida-server"
  frida_portal_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/bin/frida-portal"
  frida_inject_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/bin/frida-inject"
  frida_gadget_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/lib/frida/64/frida-gadget.so"
  package_python_script: |
    export FRIDA_EXTENSION=$(find "$FRIDA_PREFIX" -name _frida.abi3.so)
    cd subprojects/frida-python
    pip wheel -w "$FRIDA_PREFIX/wheels" --no-deps .
  frida_python_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/wheels/*.whl"
  build_node_script: |
    installdir=$FRIDA_PREFIX/prebuilds
    export FRIDA_DEPS=$PWD/deps
    export PKG_CONFIG_PATH=$FRIDA_PREFIX/libdata/pkgconfig
    python tools/ensure-submodules.py frida-node
    cd subprojects/frida-node
    npm version $FRIDA_VERSION
    ./configure "--prefix=$FRIDA_PREFIX"
    make prebuild
    mkdir "$installdir"
    cp build/*.tar.gz "$installdir"
  frida_node_freebsd_x86_64_artifacts:
    path: "$FRIDA_PREFIX/prebuilds/*"


================================================
FILE: .github/CODEOWNERS
================================================
/.github/workflows/ @oleavr


================================================
FILE: .github/FUNDING.yml
================================================
github: frida


================================================
FILE: .github/actions/package-artifact-file/action.yml
================================================
name: Package single-file artifact
description: Downloads an artifact and uploads a compressed version of its file
inputs:
  name:
    required: true
    description: Artifact name
runs:
  using: composite
  steps:
    - name: Download
      uses: actions/download-artifact@v4
      with:
        name: ${{ inputs.name }}
        path: ${{ inputs.name }}
    - name: Compress
      run: |
        readarray -t files < <(ls -1 ${{ inputs.name }})
        n=${#files[@]}
        if [ $n -ne 1 ]; then
          echo "Artifact may only contain a single file" > /dev/stderr
          exit 1
        fi
        file=${files[0]}
        if echo $file | grep -q '\.'; then
          extension=".$(echo $file | cut -d'.' -f2-).xz"
        else
          extension=".xz"
        fi
        xz -c -T 0 ${{ inputs.name }}/$file > ${{ inputs.name }}/${{ inputs.name }}$extension
        rm ${{ inputs.name }}/$file
      shell: bash
    - name: Upload
      uses: actions/upload-artifact@v4
      with:
        name: release-asset-${{ inputs.name }}
        path: ${{ inputs.name }}/*


================================================
FILE: .github/actions/package-artifact-files-as-sfx/action.yml
================================================
name: Package artifact files as SFX
description: Downloads a specific artifact and uploads a packaged version of it
inputs:
  name:
    required: true
    description: Artifact name
runs:
  using: composite
  steps:
    - name: Download
      uses: actions/download-artifact@v4
      with:
        name: ${{ inputs.name }}
        path: ${{ inputs.name }}
    - name: Compress
      run: |
        cd ${{ inputs.name }}
        7z a -sfx -r ..\${{ inputs.name }}.exe .
      shell: pwsh
    - name: Upload
      uses: actions/upload-artifact@v4
      with:
        name: release-asset-${{ inputs.name }}-sfx
        path: ${{ inputs.name }}.exe


================================================
FILE: .github/actions/package-artifact-files-as-tarball/action.yml
================================================
name: Package artifact files as tarball
description: Downloads a specific artifact and uploads a packaged version of it
inputs:
  name:
    required: true
    description: Artifact name
runs:
  using: composite
  steps:
    - name: Download
      uses: actions/download-artifact@v4
      with:
        name: ${{ inputs.name }}
        path: ${{ inputs.name }}
    - name: Compress
      run: tar -C ${{ inputs.name }} -cJf ${{ inputs.name }}.tar.xz .
      env:
        XZ_OPT: "-T 0"
      shell: bash
    - name: Upload
      uses: actions/upload-artifact@v4
      with:
        name: release-asset-${{ inputs.name }}-tarball
        path: ${{ inputs.name }}.tar.xz


================================================
FILE: .github/actions/package-ios-assets/action.yml
================================================
name: Package iOS assets
description: Downloads iOS assets and packages them
runs:
  using: composite
  steps:
    - name: Download iOS assets
      uses: actions/download-artifact@v4
      with:
        name: ios-assets
        path: build/ios-assets/
    - name: Create frida-server iOS .deb
      run: |
        cd build
        mkdir -p release-assets
        for arch in arm arm64; do
          ../subprojects/frida-core/tools/package-server-fruity.sh \
              iphoneos-$arch \
              ios-assets \
              release-assets/frida_${FRIDA_VERSION}_iphoneos-$arch.deb
        done
      shell: bash


================================================
FILE: .github/actions/package-tvos-assets/action.yml
================================================
name: Package tvOS assets
description: Downloads tvOS assets and packages them
runs:
  using: composite
  steps:
    - name: Download tvOS assets
      uses: actions/download-artifact@v4
      with:
        name: tvos-assets
        path: build/tvos-assets/
    - name: Create frida-server tvOS .deb
      run: |
        cd build
        mkdir -p release-assets
        ../subprojects/frida-core/tools/package-server-fruity.sh \
            appletvos-arm64 \
            tvos-assets \
            release-assets/frida_${FRIDA_VERSION}_appletvos-arm64.deb
      shell: bash


================================================
FILE: .github/actions/prepare-python-packages/action.yml
================================================
name: Prepare Python packages for publishing
description: Prepares Python source distribution and wheels to be published
runs:
  using: composite
  steps:
    - name: Create source distribution
      run: |
        cd subprojects/frida-python
        git submodule update --init --recursive
        python setup.py sdist --dist-dir ../../build/python-packages
      shell: bash
    - name: Download wheel for Windows/x86
      uses: actions/download-artifact@v4
      with:
        name: frida-python-windows-x86
        path: build/python-packages/
    - name: Download wheel for Windows/x86_64
      uses: actions/download-artifact@v4
      with:
        name: frida-python-windows-x86_64
        path: build/python-packages/
    - name: Download wheel for Windows/arm64
      uses: actions/download-artifact@v4
      with:
        name: frida-python-windows-arm64
        path: build/python-packages/
    - name: Download wheel for macOS/x86_64
      uses: actions/download-artifact@v4
      with:
        name: frida-python-macos-x86_64
        path: build/python-packages/
    - name: Download wheel for macOS/arm64
      uses: actions/download-artifact@v4
      with:
        name: frida-python-macos-arm64
        path: build/python-packages/
    - name: Download wheel for Linux/x86
      uses: actions/download-artifact@v4
      with:
        name: frida-python-linux-x86
        path: build/python-packages/
    - name: Download wheel for Linux/x86_64
      uses: actions/download-artifact@v4
      with:
        name: frida-python-linux-x86_64
        path: build/python-packages/
    - name: Download wheel for Linux/armhf
      uses: actions/download-artifact@v4
      with:
        name: frida-python-linux-armhf
        path: build/python-packages/
    - name: Download wheel for Linux/arm64
      uses: actions/download-artifact@v4
      with:
        name: frida-python-linux-arm64
        path: build/python-packages/


================================================
FILE: .github/actions/publish-debs/action.yml
================================================
name: Publish .deb packages
description: Publishes already packaged debs
inputs:
  site:
    required: true
    path: Where to publish; set to either “production” or “development”
runs:
  using: composite
  steps:
    - name: Create repo metadata
      run: |
        archs="iphoneos-arm iphoneos-arm64 appletvos-arm64"
        cd build
        rm -rf deb-repo-config
        mkdir deb-repo-config
        (
          echo "Origin: Cydia/Frida"
          echo "Label: frida"
          echo "Suite: stable"
          echo "Version: 1.0"
          echo "Codename: stable"
          echo "Architectures: $archs"
          echo "Components: main"
          echo "Description: Frida for iOS/tvOS"
        ) > deb-repo-config/distributions
        rm -rf deb-repo
        mkdir -p deb-repo/debs
        cd deb-repo
        (
          echo "Origin: Frida"
          echo "Label: Frida"
          echo "Suite: stable"
          echo "Version: 1.0"
          echo "Codename: ios"
          echo "Architectures: $archs"
          echo "Components: main"
          echo "Description: Official Frida Repo"
        ) > Release
        for arch in $archs; do
          file=frida_${FRIDA_VERSION}_$arch.deb
          cp $GITHUB_WORKSPACE/build/release-assets/$file debs/
          reprepro \
              -Vb . \
              --confdir $GITHUB_WORKSPACE/build/deb-repo-config \
              --ignore=forbiddenchar \
              includedeb \
              stable \
              debs/$file
        done
        (
          for arch in $archs; do
            zcat < dists/stable/main/binary-$arch/Packages.gz
          done
        ) > Packages
        gzip -9k Packages
      shell: bash
    - name: Upload package and repo metadata
      run: |
        cd build/deb-repo
        case ${{ inputs.site }} in
          production)
            prefix=
            ;;
          development)
            prefix=/dev
           ;;
          *)
            echo "Invalid repo specified" > /dev/stderr
            exit 1
        esac
        aws s3 sync --delete pool/ s3://build.frida.re${prefix}/pool/
        for file in Release Packages Packages.gz; do
          aws s3 cp $file s3://build.frida.re${prefix}/
        done
        for file in Release Packages Packages.gz; do
          aws s3 cp $file s3://build.frida.re${prefix}/./
        done
        urls_to_invalidate=()
        for file in Release Packages Packages.gz ./Release ./Packages ./Packages.gz; do
          urls_to_invalidate+=("https://build.frida.re${prefix}/${file}")
        done
        cfcli purge ${urls_to_invalidate[@]}
      shell: bash


================================================
FILE: .github/actions/setup-freebsd-env/action.yml
================================================
name: Set up FreeBSD environment
description: Set up everything needed to build and release things on FreeBSD
inputs:
  aws-access-key-id:
    required: true
    description: The aws-access-key-id used to authenticate with AWS
  aws-secret-access-key:
    required: true
    description: The aws-secret-access-key used to authenticate with AWS
  cloudflare-email:
    required: true
    description: The email used to authenticate with Cloudflare
  cloudflare-token:
    required: true
    description: The token used to authenticate with Cloudflare
runs:
  using: composite
  steps:
    - name: Configure git
      run: |
        git config --global user.name "Frida Developers"
        git config --global user.email "oleavr@frida.re"
        git config --global --add safe.directory $GITHUB_WORKSPACE
      shell: bash
    - name: Check out releng
      run: |
        git submodule update --init --depth 1 releng
        cd releng
        git submodule update --init --depth 1
      shell: bash
    - name: Add convenience environment variables
      run: |
        (
          echo "FRIDA_PREFIX=$RUNNER_WORKSPACE/dist"
          echo "FRIDA_VERSION=$(releng/frida_version.py)"
        ) >> $GITHUB_ENV
      shell: bash
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ inputs.aws-access-key-id }}
        aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
        aws-region: us-east-1
    - name: Configure Cloudflare credentials
      run: |
        (
          echo "defaults:"
          echo "    domain: frida.re"
          echo "    email: ${{ inputs.cloudflare-email }}"
          echo "    token: ${{ inputs.cloudflare-token }}"
        ) > ~/.cfcli.yml
      shell: bash


================================================
FILE: .github/actions/setup-linux-env/action.yml
================================================
name: Set up Linux environment
description: Set up everything needed to build and release things on Linux
inputs:
  aws-access-key-id:
    required: true
    description: The aws-access-key-id used to authenticate with AWS
  aws-secret-access-key:
    required: true
    description: The aws-secret-access-key used to authenticate with AWS
  cloudflare-email:
    required: true
    description: The email used to authenticate with Cloudflare
  cloudflare-token:
    required: true
    description: The token used to authenticate with Cloudflare
runs:
  using: composite
  steps:
    - name: Configure git
      run: |
        git config --global user.name "Frida Developers"
        git config --global user.email "oleavr@frida.re"
        git config --global --add safe.directory $GITHUB_WORKSPACE
      shell: bash
    - name: Check out releng
      run: |
        git submodule update --init --depth 1 releng
        cd releng
        git submodule update --init --depth 1
      shell: bash
    - name: Add convenience environment variables
      run: |
        (
          echo "FRIDA_PREFIX=$RUNNER_WORKSPACE/dist"
          echo "FRIDA_VERSION=$(releng/frida_version.py)"
        ) >> $GITHUB_ENV
      shell: bash
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ inputs.aws-access-key-id }}
        aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
        aws-region: us-east-1
    - name: Configure Cloudflare credentials
      run: |
        (
          echo "defaults:"
          echo "    domain: frida.re"
          echo "    email: ${{ inputs.cloudflare-email }}"
          echo "    token: ${{ inputs.cloudflare-token }}"
        ) > ~/.cfcli.yml
      shell: bash


================================================
FILE: .github/actions/setup-macos-env/action.yml
================================================
name: Set up macOS environment
description: Set up everything needed to build and release things on macOS
inputs:
  certificates-p12:
    required: true
    description: The certificates to use for codesigning, as a base64-encoded .p12
  certificates-password:
    required: true
    description: The password for the .p12
  keychain-password:
    required: true
    description: The keychain password to use
  aws-access-key-id:
    required: true
    description: The aws-access-key-id used to authenticate with AWS
  aws-secret-access-key:
    required: true
    description: The aws-secret-access-key used to authenticate with AWS
  cloudflare-email:
    required: true
    description: The email used to authenticate with Cloudflare
  cloudflare-token:
    required: true
    description: The token used to authenticate with Cloudflare
runs:
  using: composite
  steps:
    - name: Install the Apple certificates
      env:
        CERTIFICATES_P12: ${{ inputs.certificates-p12 }}
        CERTIFICATES_PASSWORD: ${{ inputs.certificates-password }}
        KEYCHAIN_PASSWORD: ${{ inputs.keychain-password }}
      run: |
        CERTIFICATE_PATH=$RUNNER_TEMP/apple-certificates.p12
        KEYCHAIN_PATH=$RUNNER_TEMP/frida-signing.keychain-db
        echo -n "$CERTIFICATES_P12" | base64 --decode --output $CERTIFICATE_PATH
        security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
        security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
        security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
        security import $CERTIFICATE_PATH -P "$CERTIFICATES_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
        security list-keychain -d user -s $KEYCHAIN_PATH
        rm $CERTIFICATE_PATH
        (
          MACOS_CERTID=$(security find-identity -v -p codesigning | grep "Developer ID Application: " | awk '{ print $2 }')
          IOS_CERTID=$(security find-identity -v -p codesigning | grep "Apple Development: " | awk '{ print $2 }')
          echo MACOS_CERTID=$MACOS_CERTID
          echo IOS_CERTID=$IOS_CERTID
          echo WATCHOS_CERTID=$IOS_CERTID
          echo TVOS_CERTID=$IOS_CERTID
        ) >> $GITHUB_ENV
      shell: bash
    - name: Configure git
      run: |
        git config --global user.name "Frida Developers"
        git config --global user.email "oleavr@frida.re"
      shell: bash
    - name: Check out releng
      run: |
        git submodule update --init --depth 1 releng
        cd releng
        git submodule update --init --depth 1
      shell: bash
    - name: Add convenience environment variables
      run: |
        (
          echo "FRIDA_PREFIX=$RUNNER_WORKSPACE/dist"
          echo "FRIDA_VERSION=$(releng/frida_version.py)"
        ) >> $GITHUB_ENV
      shell: bash
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ inputs.aws-access-key-id }}
        aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
        aws-region: us-east-1
    - name: Configure Cloudflare credentials
      run: |
        (
          echo "defaults:"
          echo "    domain: frida.re"
          echo "    email: ${{ inputs.cloudflare-email }}"
          echo "    token: ${{ inputs.cloudflare-token }}"
        ) > ~/.cfcli.yml
      shell: bash
    - name: Install Python
      uses: actions/setup-python@v6
      with:
        python-version: '3.12'
    - name: Install setuptools
      run: pip install setuptools
      shell: bash
    - name: Install cloudflare-cli
      run: npm install -g cloudflare-cli@5.0.5
      shell: bash


================================================
FILE: .github/actions/setup-windows-env/action.yml
================================================
name: Set up Windows environment
description: Set up everything needed to build and release things on Windows
inputs:
  aws-access-key-id:
    required: true
    description: The aws-access-key-id used to authenticate with AWS
  aws-secret-access-key:
    required: true
    description: The aws-secret-access-key used to authenticate with AWS
  cloudflare-email:
    required: true
    description: The email used to authenticate with Cloudflare
  cloudflare-token:
    required: true
    description: The token used to authenticate with Cloudflare
  architecture:
    description: The target architecture
    default: x64
runs:
  using: composite
  steps:
    - name: Configure git
      run: |
        git config --global user.name "Frida Developers"
        git config --global user.email "oleavr@frida.re"
      shell: pwsh
    - name: Check out releng
      run: |
        git submodule update --init --depth 1 releng
        cd releng
        git submodule update --init --depth 1
      shell: pwsh
    - name: Add convenience environment variables
      run: |
        echo "FRIDA_PREFIX=$Env:RUNNER_WORKSPACE\dist" >> $Env:GITHUB_ENV
        echo "FRIDA_VERSION=$(python releng/frida_version.py)" >> $Env:GITHUB_ENV
      shell: pwsh
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-access-key-id: ${{ inputs.aws-access-key-id }}
        aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
        aws-region: us-east-1
    - name: Configure Cloudflare credentials
      run: |
        @"
        defaults:
            domain: frida.re
            email: ${{ inputs.cloudflare-email }}
            token: ${{ inputs.cloudflare-token }}
        "@ | Set-Content -Path $HOME\.cfcli.yml -NoNewline
      shell: pwsh
    - name: Install Python
      uses: actions/setup-python@v6
      with:
        python-version: '3.12'
        architecture: ${{ inputs.architecture }}
    - name: Install setuptools
      run: pip install setuptools
      shell: pwsh
    - name: Install cloudflare-cli
      run: npm install -g cloudflare-cli@5.0.5
      shell: pwsh


================================================
FILE: .github/scripts/package-cirrus-ci-artifacts.sh
================================================
#!/bin/bash

sha=$1
if [ -z "$sha" ]; then
  echo "Usage: $0 <sha>" > /dev/stderr
  exit 1
fi

set -e

build_id=""
while [ -z "$build_id" ]; do
  response=$(curl https://api.cirrus-ci.com/graphql --silent --data \
        '{
          "query": "query BuildBySHAQuery($owner: String!, $name: String!, $SHA: String) { searchBuilds(repositoryOwner: $owner, repositoryName: $name, SHA: $SHA) { id, status } }",
          "variables": {
            "owner": "frida",
            "name": "frida",
            "SHA": "'$sha'"
          }
        }'
      );
  pending=0
  errors=0
  for build in $(echo $response | jq -r '.data.searchBuilds[] | { id, status } | join("|")'); do
    id=$(echo $build | cut -d'|' -f1)
    status=$(echo $build | cut -d'|' -f2)
    case $status in
      COMPLETED)
        build_id=$id
        ;;
      FAILED|ABORTED|ERRORED)
        errors=$((errors+1))
        ;;
      *)
        pending=$((pending+1))
        ;;
    esac
  done
  if [ -z "$build_id" ]; then
    if [ $errors -gt 0 ] && [ $pending -eq 0 ]; then
      echo "Cirrus CI build failed :(" > /dev/stderr
      exit 1
    else
      echo "Cirrus CI build not yet completed. Checking back in 5 minutes."
      sleep 300
    fi
  fi
done

output_dir=$PWD/build/release-assets
mkdir -p "$output_dir"

export XZ_OPT="-T 0"

for task in freebsd_x86_64; do
  host=$(echo $task | sed -e 's,_,-,')

  enter_artifact ()
  {
    local name=$1
    rm -rf build/pkg
    mkdir -p build/pkg
    pushd build/pkg > /dev/null
    local url=https://api.cirrus-ci.com/v1/artifact/build/$build_id/${name}_${task}.zip
    echo "Processing $url"
    curl $url -sLo artifact.zip
    unzip -qq artifact.zip
    rm artifact.zip
  }

  leave_artifact ()
  {
    popd > /dev/null
    rm -rf build/pkg
  }

  for component in gum gumjs core; do
    enter_artifact frida_${component}_devkit

    mv dist/lib/frida/devkits/$component/* .
    rm -rf build
    tar -cJf "$output_dir/frida-$component-devkit-$host.tar.xz" .

    leave_artifact
  done

  for component in server portal inject gadget; do
    enter_artifact frida_${component}

    readarray -t files < <(find . -type f)
    n=${#files[@]}
    if [ $n -ne 1 ]; then
      echo "The frida-$component artifact should only contain a single file" > /dev/stderr
      exit 1
    fi
    file=${files[0]}
    name=$(basename $file)
    if echo $name | grep -q '\.'; then
      extension=".$(echo $name | cut -d'.' -f2-).xz"
    else
      extension=".xz"
    fi
    xz -c -T 0 "$file" > "$output_dir/frida-$component-$host$extension"

    leave_artifact
  done

  enter_artifact frida_python
  mv dist/wheels/* "$output_dir"
  leave_artifact

  enter_artifact frida_node
  mv dist/prebuilds/* "$output_dir"
  leave_artifact
done


================================================
FILE: .github/scripts/rename-release-assets.sh
================================================
#!/bin/bash

if [ -z "$FRIDA_VERSION" ]; then
  echo "FRIDA_VERSION must be set" > /dev/stderr
  exit 1
fi

set -e

cd build/release-assets
for name in *; do
  if echo $name | grep -q $FRIDA_VERSION; then
    continue
  fi
  case $name in
    frida-*-devkit-*)
      new_name=$(echo $name | sed -e "s,devkit-,devkit-$FRIDA_VERSION-,")
      ;;
    frida-server-*|frida-portal-*|frida-inject-*|frida-gadget-*|frida-swift-*|frida-clr-*|frida-qml-*|gum-graft-*)
      new_name=$(echo $name | sed -E -e "s,^(frida|gum)-([^-]+),\\1-\\2-$FRIDA_VERSION,")
      ;;
    *)
      new_name=""
      ;;
  esac
  if [ -n "$new_name" ]; then
    mv -v $name $new_name
  fi
done


================================================
FILE: .github/scripts/repo.py
================================================
#!/usr/bin/env python3

import argparse
from configparser import ConfigParser
from pathlib import Path
import subprocess
import sys
from typing import Iterator

ROOT_DIR = Path(__file__).parent.parent.parent.resolve()
RELENG_DIR = ROOT_DIR / "releng"
if not (RELENG_DIR / "meson" / "meson.py").exists():
    subprocess.run(["git", "submodule", "update", "--init", "--depth", "1", "--recursive", "releng"],
                   cwd=ROOT_DIR)
sys.path.insert(0, str(ROOT_DIR))
from releng.deps import load_dependency_parameters, query_repo_commits


PROJECT_NAMES_IN_RELEASE_CYCLE = [
    "frida-gum",
    "frida-core",
    "frida-clr",
    "frida-node",
    "frida-python",
    "frida-qml",
    "frida-swift",
]


def main(argv: list[str]):
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    command = subparsers.add_parser("bump", help="bump all the things")
    command.set_defaults(func=lambda args: bump())

    command = subparsers.add_parser("tag", help="tag a new release")
    command.add_argument("version")
    command.set_defaults(func=lambda args: tag(args.version))

    command = subparsers.add_parser("backtag", help="retroactively tag an old release")
    command.add_argument("version")
    command.set_defaults(func=lambda args: backtag(args.version))

    args = parser.parse_args()
    if "func" in args:
        try:
            args.func(args)
        except Exception as e:
            print(e, file=sys.stderr)
            if isinstance(e, subprocess.CalledProcessError):
                for label, data in [("Output", e.output),
                                    ("Stderr", e.stderr)]:
                    if data:
                        print(f"{label}:\n\t| " + "\n\t| ".join(data.strip().split("\n")), file=sys.stderr)
            sys.exit(1)
    else:
        parser.print_usage(file=sys.stderr)
        sys.exit(1)


def bump():
    projects = list(enumerate_projects_in_release_cycle())
    projects.append(("frida-tools", ROOT_DIR / "subprojects" / "frida-tools"))

    assert_no_local_changes(ROOT_DIR)
    for _, repo in projects:
        assert_no_local_changes(repo)

    print("# releng")
    bump_releng(ROOT_DIR / "releng")
    if query_local_changes(ROOT_DIR):
        print("\tbumped")
    else:
        print("\tup-to-date")

    for name, repo in projects:
        bump_subproject(name, repo)

    if bump_submodules():
        push_changes("frida", ROOT_DIR)


def bump_subproject(name: str, repo: Path):
    print(f"# {name}")

    if not (repo / "meson.build").exists():
        run(["git", "submodule", "update", "--init", "--depth", "1", Path("subprojects") / repo], cwd=ROOT_DIR)
    run(["git", "checkout", "main"], cwd=repo)
    run(["git", "pull"], cwd=repo)

    releng = repo / "releng"
    bump_releng(releng)
    if query_local_changes(repo):
        run(["git", "submodule", "update"], cwd=releng)
        run(["git", "add", "releng"], cwd=repo)
        run(["git", "commit", "-m", "submodules: Bump releng"], cwd=repo)
        print("\treleng: bumped")
    else:
        print("\treleng: up-to-date")

    bumped_files: list[Path] = []
    dep_packages = load_dependency_parameters().packages
    for identifier, config, wrapfile in enumerate_git_wraps_in_repo(repo):
        if identifier == "nan":
            continue

        source = config["wrap-git"]

        pkg = dep_packages.get(identifier)
        if pkg is not None:
            current_revision = pkg.version
        else:
            other_repo = ROOT_DIR / "subprojects" / identifier
            if other_repo.exists():
                current_revision = run(["git", "rev-parse", "HEAD"], cwd=other_repo).stdout.strip()
            else:
                url = source["url"]
                assert url.startswith("https://github.com/"), f"{url}: unhandled repo URL"
                assert url.endswith(".git")
                tokens = url[19:-4].split("/")
                assert len(tokens) == 2
                current_revision = query_repo_commits(organization=tokens[0], repo=tokens[1])["sha"]

        if source["revision"] != current_revision:
            source["revision"] = current_revision
            with wrapfile.open("w") as f:
                config.write(f)
            bumped_files.append(wrapfile)

    if bumped_files:
        run(["git", "add", *bumped_files], cwd=repo)
        run(["git", "commit", "-m", "subprojects: Bump outdated"], cwd=repo)
        print(f"\tsubprojects: bumped {', '.join([f.stem for f in bumped_files])}")
    else:
        print("\tsubprojects: up-to-date")

    push_changes(name, repo)


def bump_releng(releng: Path):
    if not (releng / "meson" / "meson.py").exists():
        run(["git", "submodule", "update", "--init", "--depth", "1", "--recursive", "releng"], cwd=releng.parent)
    run(["git", "checkout", "main"], cwd=releng)
    run(["git", "pull"], cwd=releng)


def bump_submodules() -> list[str]:
    print("# submodules")
    changes = query_local_changes(ROOT_DIR)
    relevant_changes = [relpath for kind, relpath in changes
                        if kind == "M" and (relpath == "releng" or relpath.startswith("subprojects/"))]
    assert len(changes) == len(relevant_changes), "frida: expected clean repo"
    if relevant_changes:
        run(["git", "add", *relevant_changes], cwd=ROOT_DIR)
        run(["git", "commit", "-m", "submodules: Bump outdated"], cwd=ROOT_DIR)
        print(f"\tbumped {', '.join([Path(relpath).name for relpath in relevant_changes])}")
    else:
        print("\tup-to-date")
    return relevant_changes


def tag(version: str):
    for _, repo in enumerate_projects_in_release_cycle():
        assert_no_local_changes(repo)
    for name, repo in enumerate_projects_in_release_cycle():
        prepublish(name, version, repo)

    bump_submodules()

    prepublish("frida", version, ROOT_DIR)


def prepublish(name: str, version: str, repo: Path):
    print("Prepublishing:", name)

    modified_wrapfiles: list[Path] = []
    for identifier, config, wrapfile in enumerate_git_wraps_in_repo(repo):
        if identifier in PROJECT_NAMES_IN_RELEASE_CYCLE:
            config["wrap-git"]["revision"] = version
            with wrapfile.open("w") as f:
                config.write(f)
            modified_wrapfiles.append(wrapfile)

    if modified_wrapfiles:
        run(["git", "add", *modified_wrapfiles], cwd=repo)
        run(["git", "commit", "-m", "subprojects: Prepare for release"], cwd=repo)
        print(f"\tsubprojects: prepared {', '.join([f.stem for f in modified_wrapfiles])}")
    else:
        print("\tsubprojects: no changes needed")

    run(["git", "tag", version], cwd=repo)
    run(["git", "push", "--atomic", "origin", "main", version], cwd=repo)
    print("\tpushed")


def backtag(version: str):
    run(["git", "checkout", version], cwd=ROOT_DIR)
    run(["git", "submodule", "update"], cwd=ROOT_DIR)
    for name, repo in enumerate_projects_in_release_cycle():
        if not run(["git", "tag", "-l", version], cwd=repo).stdout.strip():
            run(["git", "tag", version], cwd=repo)
            ensure_remote_origin_writable(name, repo)
            run(["git", "push", "origin", version], cwd=repo)


def enumerate_projects_in_release_cycle() -> Iterator[tuple[str, Path]]:
    for name in PROJECT_NAMES_IN_RELEASE_CYCLE:
        yield name, ROOT_DIR / "subprojects" / name


def enumerate_git_wraps_in_repo(repo: Path) -> Iterator[tuple[str, ConfigParser, Path]]:
    for wrapfile in (repo / "subprojects").glob("*.wrap"):
        identifier = wrapfile.stem

        config = ConfigParser()
        config.read(wrapfile)

        if "wrap-git" not in config:
            continue

        yield identifier, config, wrapfile


def assert_no_local_changes(repo: Path):
    assert not query_local_changes(repo), f"{repo.name}: expected clean repo"


def query_local_changes(repo: Path) -> list[str]:
    output = run(["git", "status", "--porcelain=v1"], cwd=repo).stdout.strip()
    if not output:
        return []
    return [tuple(line.strip().split(" ", maxsplit=1)) for line in output.split("\n")]


def push_changes(name: str, repo: Path):
    ensure_remote_origin_writable(name, repo)
    run(["git", "push", "-u", "origin", "main"], cwd=repo)


def ensure_remote_origin_writable(name: str, repo: Path):
    if "https:" in run(["git", "remote", "show", "origin", "-n"], cwd=repo).stdout:
        run(["git", "remote", "rm", "origin"], cwd=repo)
        run(["git", "remote", "add", "origin", f"git@github.com:frida/{name}.git"], cwd=repo)
        run(["git", "fetch", "origin"], cwd=repo)


def run(argv: list[str], **kwargs) -> subprocess.CompletedProcess:
    return subprocess.run(argv,
                          capture_output=True,
                          encoding="utf-8",
                          check=True,
                          **kwargs)


if __name__ == "__main__":
    main(sys.argv)


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on: push

env:
  FRIDA_COMPILER_MOD: subprojects/frida-core/src/compiler/go.mod
  ANDROID_NDK_VERSION: r29

jobs:
  publish-prod:
    if: startsWith(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest
    needs:
      - package-windows
      - package-windows-sfx
      - package-macos
      - package-linux
      - package-ios
      - package-watchos
      - package-tvos
      - package-android
      - package-qnx
      - package-apple-universal
      - assemble-ios-assets
      - assemble-tvos-assets
    permissions:
      contents: write
      id-token: write
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Grab needed submodules
        run: python tools/ensure-submodules.py frida-gum frida-core frida-python frida-node
      - name: Install tools
        run: |
          sudo apt-get update
          sudo apt-get install -y reprepro
          sudo npm install -g cloudflare-cli@5.0.5
      - name: Download release assets
        uses: actions/download-artifact@v4
        with:
          pattern: release-asset-*
          merge-multiple: true
          path: build/release-assets/
      - name: Download Node-API prebuilds for Windows/x86_64
        uses: actions/download-artifact@v4
        with:
          name: frida-node-windows-x86_64
          path: build/release-assets/
      - name: Download Node-API prebuilds for Windows/arm64
        uses: actions/download-artifact@v4
        with:
          name: frida-node-windows-arm64
          path: build/release-assets/
      - name: Download Node-API prebuilds for macOS/x86_64
        uses: actions/download-artifact@v4
        with:
          name: frida-node-macos-x86_64
          path: build/release-assets/
      - name: Download Node-API prebuilds for macOS/arm64
        uses: actions/download-artifact@v4
        with:
          name: frida-node-macos-arm64
          path: build/release-assets/
      - name: Download Node-API prebuilds for Linux/x86
        uses: actions/download-artifact@v4
        with:
          name: frida-node-linux-x86
          path: build/release-assets/
      - name: Download Node-API prebuilds for Linux/x86_64
        uses: actions/download-artifact@v4
        with:
          name: frida-node-linux-x86_64
          path: build/release-assets/
      - name: Download Node-API prebuilds for Linux/armhf
        uses: actions/download-artifact@v4
        with:
          name: frida-node-linux-armhf
          path: build/release-assets/
      - name: Download Node-API prebuilds for Linux/arm64
        uses: actions/download-artifact@v4
        with:
          name: frida-node-linux-arm64
          path: build/release-assets/
      - name: Package iOS assets
        uses: ./.github/actions/package-ios-assets
      - name: Package tvOS assets
        uses: ./.github/actions/package-tvos-assets
      - name: Rename release assets
        run: .github/scripts/rename-release-assets.sh
      - name: Publish release to GitHub
        uses: softprops/action-gh-release@v1
        with:
          name: "Frida ${{ env.FRIDA_VERSION }}"
          body: "See https://frida.re/news/ for details."
          files: build/release-assets/*
      - name: Prepare Python packages
        uses: ./.github/actions/prepare-python-packages
      - name: Publish Python bindings to pypi.org
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: build/python-packages/
      - name: Publish .deb packages
        uses: ./.github/actions/publish-debs
        with:
          site: production
      - name: Trigger frida-node publish
        env:
          GH_TOKEN: ${{ secrets.FRIDA_NODE_BOT_PAT }}
          FRIDA_VERSION: ${{ env.FRIDA_VERSION }}
        run: |
          gh api repos/frida/frida-node/dispatches \
              -f event_type=publish-prod-started \
              -f client_payload[frida_version]="${FRIDA_VERSION}"
      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 24
      - name: Publish frida-gadget-ios to npm
        run: |
          cd releng/modules/frida-gadget-ios
          npm version $FRIDA_VERSION
          npm publish
      - name: Trigger magisk-frida CI
        run: |
          curl \
              -X POST \
              -H "Accept: application/vnd.github.v3+json" \
              -H "Authorization: token ${{ secrets.MAGISK_FRIDA_TOKEN }}" \
              https://api.github.com/repos/ViRb3/magisk-frida/dispatches \
              -d '{"event_type":"build"}'

  publish-dev:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    needs:
      - frida-windows
      - frida-macos
      - frida-linux
      - assemble-ios-assets
      - assemble-tvos-assets
    permissions:
      id-token: write
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Grab needed submodules
        run: python tools/ensure-submodules.py frida-core frida-python
      - name: Install tools
        run: |
          sudo apt-get update
          sudo apt-get install -y reprepro
          sudo npm install -g cloudflare-cli@5.0.5
      - name: Package iOS assets
        uses: ./.github/actions/package-ios-assets
      - name: Package tvOS assets
        uses: ./.github/actions/package-tvos-assets
      - name: Prepare Python packages
        uses: ./.github/actions/prepare-python-packages
      - name: Publish Python bindings to TestPyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: build/python-packages/
          repository-url: https://test.pypi.org/legacy/
      - name: Publish .deb packages
        uses: ./.github/actions/publish-debs
        with:
          site: development

  assemble-ios-assets:
    runs-on: macos-latest
    needs: frida-ios
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Download universal frida-server for iOS/arm64(e)
        uses: actions/download-artifact@v4
        with:
          name: frida-server-ios-arm64e
          path: ios-arm64e/
      - name: Create universal frida-server for iOS
        run: |
          lipo ios-arm64e/frida-server -thin arm64 -output frida-server-arm64
          lipo ios-arm64e/frida-server -thin arm64e -output frida-server-arm64e
          for arch in arm64 arm64e; do
            codesign -f -s "-" --preserve-metadata=entitlements frida-server-$arch
          done
          ./releng/mkfatmacho.py \
              frida-server-ios-universal \
              frida-server-arm64 \
              frida-server-arm64e
      - name: Download universal frida-agent for iOS/arm64(e)
        uses: actions/download-artifact@v4
        with:
          name: frida-agent-ios-arm64e
          path: ios-arm64e/
      - name: Sign universal frida-agent for iOS/arm64(e)
        run: codesign -f -s "-" ios-arm64e/frida-agent.dylib
      - name: Assemble iOS assets
        run: |
          mkdir -p ios-assets/usr/bin ios-assets/usr/lib/frida
          mv frida-server-ios-universal ios-assets/usr/bin/frida-server
          mv ios-arm64e/frida-agent.dylib ios-assets/usr/lib/frida/frida-agent.dylib
      - name: Upload iOS assets
        uses: actions/upload-artifact@v4
        with:
          name: ios-assets
          path: ios-assets/*

  assemble-tvos-assets:
    runs-on: macos-latest
    needs: frida-tvos
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Download frida-server for tvOS/arm64
        uses: actions/download-artifact@v4
        with:
          name: frida-server-tvos-arm64
          path: tvos-arm64/
      - name: Prepare frida-server for tvOS/arm64
        run: codesign -f -s "-" --preserve-metadata=entitlements tvos-arm64/frida-server
      - name: Download frida-agent for tvOS/arm64
        uses: actions/download-artifact@v4
        with:
          name: frida-agent-tvos-arm64
          path: tvos-arm64/
      - name: Prepare frida-agent for tvOS/arm64
        run: codesign -f -s "-" tvos-arm64/frida-agent.dylib
      - name: Assemble tvOS assets
        run: |
          mkdir -p tvos-assets/usr/bin tvos-assets/usr/lib/frida
          mv tvos-arm64/frida-server tvos-assets/usr/bin/frida-server
          mv tvos-arm64/frida-agent.dylib tvos-assets/usr/lib/frida/frida-agent.dylib
      - name: Upload tvOS assets
        uses: actions/upload-artifact@v4
        with:
          name: tvos-assets
          path: tvos-assets/*

  package-apple-universal:
    if: startsWith(github.ref, 'refs/tags/')
    runs-on: macos-latest
    needs: [frida-macos, frida-ios, frida-watchos, frida-tvos]
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Download frida-gadget for macOS/x86_64
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-macos-x86_64
          path: macos-x86_64/
      - name: Download frida-gadget for macOS/arm64(e)
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-macos-arm64e
          path: macos-arm64e/
      - name: Create universal frida-gadget for macOS
        run: |
          lipo \
              macos-x86_64/frida-gadget.dylib \
              macos-arm64e/frida-gadget.dylib \
              -create -output frida-gadget-macos-universal.dylib
          install_name_tool \
              -id @executable_path/../Frameworks/FridaGadget.dylib \
              frida-gadget-macos-universal.dylib
          codesign \
              -f -s "$MACOS_CERTID" \
              frida-gadget-macos-universal.dylib
      - name: Compress universal frida-gadget for macOS
        run: xz -T 0 frida-gadget-macos-universal.dylib
      - name: Upload universal frida-gadget for macOS
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-macos-universal
          path: frida-gadget-macos-universal.dylib.xz
      - name: Download frida-gadget for iOS/arm64(e)
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-ios-arm64e
          path: ios-arm64e/
      - name: Create universal frida-gadget for iOS
        run: |
          mv ios-arm64e/frida-gadget.dylib frida-gadget-ios-universal.dylib
          install_name_tool \
              -id @executable_path/Frameworks/FridaGadget.dylib \
              frida-gadget-ios-universal.dylib
          codesign \
              -f -s "$IOS_CERTID" \
              frida-gadget-ios-universal.dylib
      - name: Compress universal frida-gadget for iOS
        run: |
          gzip -k frida-gadget-ios-universal.dylib
          xz -T 0 frida-gadget-ios-universal.dylib
      - name: Upload universal frida-gadget for iOS (gz)
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-ios-universal-gz
          path: frida-gadget-ios-universal.dylib.gz
      - name: Upload universal frida-gadget for iOS (xz)
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-ios-universal-xz
          path: frida-gadget-ios-universal.dylib.xz
      - name: Download frida-gadget for iOS/x86_64-simulator
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-ios-x86_64-simulator
          path: ios-x86_64-simulator/
      - name: Download frida-gadget for iOS/arm64-simulator
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-ios-arm64-simulator
          path: ios-arm64-simulator/
      - name: Create universal frida-gadget for iOS Simulator
        run: |
          lipo \
              ios-x86_64-simulator/frida-gadget.dylib \
              ios-arm64-simulator/frida-gadget.dylib \
              -create -output frida-gadget-ios-simulator-universal.dylib
          install_name_tool \
              -id @executable_path/Frameworks/FridaGadget.dylib \
              frida-gadget-ios-simulator-universal.dylib
          codesign \
              -f -s "$IOS_CERTID" \
              frida-gadget-ios-simulator-universal.dylib
      - name: Compress universal frida-gadget for iOS Simulator
        run: xz -T 0 frida-gadget-ios-simulator-universal.dylib
      - name: Upload universal frida-gadget for iOS Simulator
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-ios-simulator-universal
          path: frida-gadget-ios-simulator-universal.dylib.xz
      - name: Download frida-gadget for watchOS/arm64
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-watchos-arm64
          path: watchos-arm64/
      - name: Download frida-gadget for watchOS/arm64-simulator
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-watchos-arm64-simulator
          path: watchos-arm64-simulator/
      - name: Package frida-gadget for watchOS
        run: |
          for arch in arm64 arm64-simulator; do
            cp watchos-$arch/frida-gadget.dylib frida-gadget-watchos-$arch.dylib
            install_name_tool \
                -id @executable_path/Frameworks/FridaGadget.dylib \
                frida-gadget-watchos-$arch.dylib
            codesign \
                -f -s "$WATCHOS_CERTID" \
                frida-gadget-watchos-$arch.dylib
            xz -T 0 frida-gadget-watchos-$arch.dylib
          done
      - name: Upload frida-gadget for watchOS/arm64
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-watchos-arm64
          path: frida-gadget-watchos-arm64.dylib.xz
      - name: Upload frida-gadget for watchOS/arm64-simulator
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-watchos-arm64-simulator
          path: frida-gadget-watchos-arm64-simulator.dylib.xz
      - name: Download frida-gadget for tvOS/arm64
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-tvos-arm64
          path: tvos-arm64/
      - name: Download frida-gadget for tvOS/arm64-simulator
        uses: actions/download-artifact@v4
        with:
          name: frida-gadget-tvos-arm64-simulator
          path: tvos-arm64-simulator/
      - name: Package frida-gadget for tvOS
        run: |
          for arch in arm64 arm64-simulator; do
            cp tvos-$arch/frida-gadget.dylib frida-gadget-tvos-$arch.dylib
            install_name_tool \
                -id @executable_path/Frameworks/FridaGadget.dylib \
                frida-gadget-tvos-$arch.dylib
            codesign \
                -f -s "$TVOS_CERTID" \
                frida-gadget-tvos-$arch.dylib
            xz -T 0 frida-gadget-tvos-$arch.dylib
          done
      - name: Upload frida-gadget for tvOS/arm64
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-tvos-arm64
          path: frida-gadget-tvos-arm64.dylib.xz
      - name: Upload frida-gadget for tvOS/arm64-simulator
        uses: actions/upload-artifact@v4
        with:
          name: release-asset-frida-gadget-tvos-arm64-simulator
          path: frida-gadget-tvos-arm64-simulator.dylib.xz

  package-windows:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-windows
    strategy:
      matrix:
        arch: [x86, x86_64, arm64]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-windows-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-windows-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-windows-${{ matrix.arch }}
      - name: Package frida-server
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-server-windows-${{ matrix.arch }}
      - name: Package frida-portal
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-portal-windows-${{ matrix.arch }}
      - name: Package frida-inject
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-inject-windows-${{ matrix.arch }}
      - name: Package frida-gadget
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-gadget-windows-${{ matrix.arch }}
      - name: Package .NET bindings
        if: matrix.arch != 'arm64'
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-clr-windows-${{ matrix.arch }}
      - name: Package QML bindings
        if: matrix.arch == 'x86_64'
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-qml-windows-${{ matrix.arch }}

  package-windows-sfx:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-windows
    strategy:
      matrix:
        arch: [x86, x86_64]
    runs-on: windows-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-sfx
        with:
          name: frida-gum-devkit-windows-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-sfx
        with:
          name: frida-gumjs-devkit-windows-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-sfx
        with:
          name: frida-core-devkit-windows-${{ matrix.arch }}

  package-macos:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-macos
    strategy:
      matrix:
        arch: [x86_64, arm64, arm64e]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-macos-${{ matrix.arch }}
      - name: Package gum-graft
        if: matrix.arch != 'arm64e'
        uses: ./.github/actions/package-artifact-file
        with:
          name: gum-graft-macos-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-macos-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-macos-${{ matrix.arch }}
      - name: Package frida-server
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-server-macos-${{ matrix.arch }}
      - name: Package frida-portal
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-portal-macos-${{ matrix.arch }}
      - name: Package frida-inject
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-inject-macos-${{ matrix.arch }}
      - name: Package QML bindings
        if: matrix.arch == 'x86_64'
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-qml-macos-${{ matrix.arch }}

  package-linux:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-linux
    strategy:
      matrix:
        arch: [x86, x86_64, x86_64-musl, armhf, armhf-musl, armbe8, arm64, arm64be, arm64-musl, mips, mipsel, mips64, mips64el]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-linux-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-linux-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-linux-${{ matrix.arch }}
      - name: Package frida-server
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-server-linux-${{ matrix.arch }}
      - name: Package frida-portal
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-portal-linux-${{ matrix.arch }}
      - name: Package frida-gadget
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-gadget-linux-${{ matrix.arch }}
      - name: Package frida-inject
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-inject-linux-${{ matrix.arch }}
      - name: Package QML bindings
        if: matrix.arch == 'x86_64'
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-qml-linux-${{ matrix.arch }}

  package-ios:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-ios
    strategy:
      matrix:
        arch: [arm64, arm64e, x86_64-simulator, arm64-simulator]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-ios-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-ios-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-ios-${{ matrix.arch }}
      - name: Package frida-portal
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-portal-ios-${{ matrix.arch }}

  package-watchos:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-watchos
    strategy:
      matrix:
        arch: [arm64, arm64-simulator]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-watchos-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-watchos-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-watchos-${{ matrix.arch }}

  package-tvos:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-tvos
    strategy:
      matrix:
        arch: [arm64, arm64-simulator]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-tvos-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-tvos-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-tvos-${{ matrix.arch }}

  package-android:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-android
    strategy:
      matrix:
        arch: [x86, x86_64, arm, arm64]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-android-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-android-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-android-${{ matrix.arch }}
      - name: Package frida-server
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-server-android-${{ matrix.arch }}
      - name: Package frida-portal
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-portal-android-${{ matrix.arch }}
      - name: Package frida-gadget
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-gadget-android-${{ matrix.arch }}
      - name: Package frida-inject
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-inject-android-${{ matrix.arch }}

  package-qnx:
    if: startsWith(github.ref, 'refs/tags/')
    needs: frida-qnx
    strategy:
      matrix:
        arch: [armeabi]
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Package Gum devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gum-devkit-qnx-${{ matrix.arch }}
      - name: Package GumJS devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-gumjs-devkit-qnx-${{ matrix.arch }}
      - name: Package Core devkit
        uses: ./.github/actions/package-artifact-files-as-tarball
        with:
          name: frida-core-devkit-qnx-${{ matrix.arch }}
      - name: Package frida-server
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-server-qnx-${{ matrix.arch }}
      - name: Package frida-portal
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-portal-qnx-${{ matrix.arch }}
      - name: Package frida-gadget
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-gadget-qnx-${{ matrix.arch }}
      - name: Package frida-inject
        uses: ./.github/actions/package-artifact-file
        with:
          name: frida-inject-qnx-${{ matrix.arch }}

  frida-windows-intel:
    needs: sdk-windows
    strategy:
      matrix:
        include:
          - { arch: x86,    sys: MINGW32,    pkg: 'mingw-w64-i686-gcc'            }
          - { arch: x86_64, sys: MINGW64,    pkg: 'mingw-w64-x86_64-gcc'          }
      fail-fast: false
    runs-on: windows-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-windows-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
          architecture: ${{ matrix.arch == 'x86_64' && 'x64' || matrix.arch }}
      - name: Install MinGW toolchain for cgo support
        id: msys2
        uses: msys2/setup-msys2@v2
        with:
          msystem: ${{ matrix.sys }}
          install: ${{ matrix.pkg }}
      - name: Grab frida-core
        run: python tools/ensure-submodules.py frida-core
      - name: Install Go toolchain
        uses: actions/setup-go@v5
        with:
          go-version-file: ${{ env.FRIDA_COMPILER_MOD }}
          cache-dependency-path: ${{ env.FRIDA_COMPILER_MOD }}
      - name: Install Qt
        if: matrix.arch == 'x86_64'
        uses: jurplel/install-qt-action@v4
        with:
          version: '6.7.0'
          cache: true
          setup-python: false
      - name: Configure
        run: >-
          .\configure
          --prefix=$Env:FRIDA_PREFIX
          --host=windows-${{ matrix.arch }}
          --enable-gadget
          --enable-server
          --enable-portal
          --enable-inject
          --
          "-Dfrida_qml=auto"
          "-Dfrida-gum:devkits=gum,gumjs"
          "-Dfrida-core:compiler_backend=enabled"
          "-Dfrida-core:devkits=core"
        env:
          MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }}
      - name: Compile
        run: .\make
      - name: Install
        run: .\make install
      - name: Upload components needed for emulation on arm64
        if: matrix.arch == 'x86_64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-emulation-windows
          path: |
            build/subprojects/frida-core/src/frida-helper.exe
            build/subprojects/frida-core/lib/agent/frida-agent.dll
            build/subprojects/frida-core/compat/frida-helper.exe
            build/subprojects/frida-core/compat/frida-agent.dll
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/core/
      - name: Upload frida-server
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-server.exe
      - name: Upload frida-portal
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-portal.exe
      - name: Upload frida-inject
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-inject.exe
      - name: Upload 32-bit frida-gadget
        if: matrix.arch != 'x86_64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/32/frida-gadget.dll
      - name: Upload 64-bit frida-gadget
        if: matrix.arch == 'x86_64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/64/frida-gadget.dll
      - name: Package Python bindings
        run: |
          $Env:FRIDA_EXTENSION = "$Env:FRIDA_PREFIX\lib\site-packages\frida\_frida.pyd"
          cd subprojects\frida-python
          pip wheel -w "$Env:FRIDA_PREFIX\wheels" --no-deps .
      - name: Upload Python bindings
        uses: actions/upload-artifact@v4
        with:
          name: frida-python-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/wheels/*.whl
      - name: Build Node.js bindings
        if: matrix.arch == 'x86_64'
        run: |
          $installdir = "$Env:FRIDA_PREFIX\prebuilds"
          $Env:FRIDA_DEPS = "$pwd\deps"
          $Env:PKG_CONFIG_PATH = "$Env:FRIDA_PREFIX\lib\pkgconfig"
          function Run {
            param(
              [Parameter(Mandatory)]
              [string] $FilePath,
              [Parameter(ValueFromRemainingArguments = $true)]
              [string[]] $Argv
            )
            $p = Start-Process -FilePath $FilePath -ArgumentList $Argv -NoNewWindow -Wait -PassThru
            if ($p.ExitCode -ne 0) {
              throw "'$FilePath $($Argv -join ' ')' failed with exit status $($p.ExitCode)"
            }
          }
          Run python .\tools\ensure-submodules.py frida-node
          cd subprojects\frida-node
          Run npm.cmd version $Env:FRIDA_VERSION
          Run .\configure `
              --prefix=$Env:FRIDA_PREFIX `
              --host=windows-${{ matrix.arch }}
          Run .\make prebuild
          New-Item -ItemType Directory -Path $installdir
          Copy-Item .\build\*.tar.gz -Destination $installdir
      - name: Upload Node.js bindings
        if: matrix.arch == 'x86_64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-node-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/prebuilds/*.tar.gz
      - name: Build .NET bindings
        run: >-
          Remove-Item build -Recurse
          && .\configure
          --prefix=$Env:FRIDA_PREFIX
          --host=windows-${{ matrix.arch }}-md
          --enable-frida-clr
          --disable-frida-python
          --disable-frida-tools
          --
          "-Dfrida-core:compiler_backend=disabled"
          && .\make install
      - name: Upload .NET bindings
        uses: actions/upload-artifact@v4
        with:
          name: frida-clr-windows-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/Frida.dll
      - name: Upload QML bindings
        if: matrix.arch == 'x86_64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-qml-windows-${{ matrix.arch }}
          path: |
            ${{ env.FRIDA_PREFIX }}/lib/qml/
            !*.lib
            !*.pdb

  frida-windows:
    needs: frida-windows-intel
    runs-on: windows-11-arm
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-windows-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
          architecture: arm64
      - name: Install MinGW toolchain for cgo support
        id: msys2
        uses: msys2/setup-msys2@v2
        with:
          msystem: CLANGARM64
          install: mingw-w64-clang-aarch64-clang
      - name: Grab frida-core
        run: python tools/ensure-submodules.py frida-core
      - name: Install Go toolchain
        uses: actions/setup-go@v5
        with:
          go-version-file: ${{ env.FRIDA_COMPILER_MOD }}
          cache-dependency-path: ${{ env.FRIDA_COMPILER_MOD }}
      - name: Download emulation components
        uses: actions/download-artifact@v4
        with:
          name: frida-core-emulation-windows
          path: emulation/
      - name: Configure
        run: |
          $helperModern = (Resolve-Path "emulation/src/frida-helper.exe").Path
          $agentModern = (Resolve-Path "emulation/lib/agent/frida-agent.dll").Path
          $helperLegacy = (Resolve-Path "emulation/compat/frida-helper.exe").Path
          $agentLegacy = (Resolve-Path "emulation/compat/frida-agent.dll").Path

          .\configure `
            --prefix=$Env:FRIDA_PREFIX `
            --host=windows-arm64 `
            --enable-gadget `
            --enable-server `
            --enable-portal `
            --enable-inject `
            -- `
            "-Dfrida_qml=auto" `
            "-Dfrida-gum:devkits=gum,gumjs" `
            "-Dfrida-core:compiler_backend=enabled" `
            "-Dfrida-core:devkits=core" `
            "-Dfrida-core:helper_emulated_modern=$helperModern" `
            "-Dfrida-core:agent_emulated_modern=$agentModern" `
            "-Dfrida-core:helper_emulated_legacy=$helperLegacy" `
            "-Dfrida-core:agent_emulated_legacy=$agentLegacy"
        env:
          MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }}
      - name: Compile
        run: .\make
      - name: Install
        run: .\make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/core/
      - name: Upload frida-server
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-server.exe
      - name: Upload frida-portal
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-portal.exe
      - name: Upload frida-inject
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-inject.exe
      - name: Upload frida-gadget
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/64/frida-gadget.dll
      - name: Package Python bindings
        run: |
          $Env:FRIDA_EXTENSION = "$Env:FRIDA_PREFIX\lib\site-packages\frida\_frida.pyd"
          cd subprojects\frida-python
          pip wheel -w "$Env:FRIDA_PREFIX\wheels" --no-deps .
      - name: Upload Python bindings
        uses: actions/upload-artifact@v4
        with:
          name: frida-python-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/wheels/*.whl
      - name: Build Node.js bindings
        run: |
          $installdir = "$Env:FRIDA_PREFIX\prebuilds"
          $Env:FRIDA_DEPS = "$pwd\deps"
          $Env:PKG_CONFIG_PATH = "$Env:FRIDA_PREFIX\lib\pkgconfig"
          function Run {
            param(
              [Parameter(Mandatory)]
              [string] $FilePath,
              [Parameter(ValueFromRemainingArguments = $true)]
              [string[]] $Argv
            )
            $p = Start-Process -FilePath $FilePath -ArgumentList $Argv -NoNewWindow -Wait -PassThru
            if ($p.ExitCode -ne 0) {
              throw "'$FilePath $($Argv -join ' ')' failed with exit status $($p.ExitCode)"
            }
          }
          Run python .\tools\ensure-submodules.py frida-node
          cd subprojects\frida-node
          Run npm.cmd version $Env:FRIDA_VERSION
          Run .\configure `
              --prefix=$Env:FRIDA_PREFIX `
              --host=windows-arm64
          Run .\make prebuild
          New-Item -ItemType Directory -Path $installdir
          Copy-Item .\build\*.tar.gz -Destination $installdir
      - name: Upload Node.js bindings
        uses: actions/upload-artifact@v4
        with:
          name: frida-node-windows-arm64
          path: ${{ env.FRIDA_PREFIX }}/prebuilds/*.tar.gz

  frida-macos:
    needs: sdk-macos
    strategy:
      matrix:
        arch: [x86_64, arm64, arm64e]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Grab frida-core
        run: python tools/ensure-submodules.py frida-core
      - name: Install Go toolchain
        uses: actions/setup-go@v5
        with:
          go-version-file: ${{ env.FRIDA_COMPILER_MOD }}
          cache-dependency-path: ${{ env.FRIDA_COMPILER_MOD }}
      - name: Install Qt
        if: matrix.arch != 'arm64e'
        uses: jurplel/install-qt-action@v4
        with:
          version: '6.7.0'
          cache: true
          setup-python: false
      - name: Configure
        run: >-
          ./configure
          "--prefix=$FRIDA_PREFIX"
          --host=macos-${{ matrix.arch }}
          --enable-gadget
          --enable-server
          --enable-portal
          --enable-inject
          --enable-frida-python
          --
          -Dfrida_qml=${{ matrix.arch != 'arm64e' && 'enabled' || 'disabled' }}
          -Dfrida-gum:graft_tool=enabled
          -Dfrida-gum:devkits=gum,gumjs
          -Dfrida-core:compiler_backend=${{ matrix.arch != 'arm64e' && 'enabled' || 'disabled' }}
          -Dfrida-core:devkits=core
      - name: Compile
        run: make
      - name: Install
        run: make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gum/
      - name: Upload gum-graft
        if: matrix.arch != 'arm64e'
        uses: actions/upload-artifact@v4
        with:
          name: gum-graft-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/gum-graft
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/core/
      - name: Upload frida-server
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-server
      - name: Upload frida-portal
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-portal
      - name: Upload frida-inject
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-inject
      - name: Upload frida-gadget
        if: matrix.arch != 'arm64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/frida-gadget.dylib
      - name: Package Python bindings
        run: |
          export FRIDA_EXTENSION=$(find "$FRIDA_PREFIX" -name _frida.abi3.so)
          case ${{ matrix.arch }} in
            x86_64)
              platform=macosx-10.13-x86_64
              ;;
            arm64*)
              platform=macosx-11.0-arm64
              ;;
          esac
          export _PYTHON_HOST_PLATFORM=$platform
          cd subprojects/frida-python
          pip wheel -w "$FRIDA_PREFIX/wheels" --no-deps .
      - name: Upload Python bindings
        uses: actions/upload-artifact@v4
        with:
          name: frida-python-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/wheels/*.whl
      - name: Build Node.js bindings
        if: matrix.arch != 'arm64e'
        run: |
          installdir=$FRIDA_PREFIX/prebuilds
          export FRIDA_DEPS=$PWD/deps
          export PKG_CONFIG_PATH=$FRIDA_PREFIX/lib/pkgconfig
          python tools/ensure-submodules.py frida-node
          cd subprojects/frida-node
          npm version $FRIDA_VERSION
          ./configure \
              "--prefix=$FRIDA_PREFIX" \
              --host=macos-${{ matrix.arch }}
          make prebuild
          mkdir "$installdir"
          cp build/*.tar.gz "$installdir"
        shell: zsh {0}
      - name: Upload Node.js bindings
        if: matrix.arch != 'arm64e'
        uses: actions/upload-artifact@v4
        with:
          name: frida-node-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/prebuilds/*.tar.gz
      - name: Upload QML bindings
        if: matrix.arch != 'arm64e'
        uses: actions/upload-artifact@v4
        with:
          name: frida-qml-macos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/qml/

  frida-linux:
    needs: sdk-linux
    strategy:
      matrix:
        arch: [x86, x86_64, x86_64-musl, armhf, armhf-musl, armbe8, arm64, arm64be, arm64-musl, mips, mipsel, mips64, mips64el]
      fail-fast: false
    runs-on: ubuntu-latest
    container: ghcr.io/frida/x-tools-linux-${{ matrix.arch }}:latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Grab frida-core
        run: python tools/ensure-submodules.py frida-core
      - name: Install Go toolchain
        uses: actions/setup-go@v5
        with:
          go-version-file: ${{ env.FRIDA_COMPILER_MOD }}
          cache-dependency-path: ${{ env.FRIDA_COMPILER_MOD }}
      - name: Configure qemu-user
        if: ${{ !startsWith(matrix.arch, 'x86') }}
        run: echo "FRIDA_QEMU_SYSROOT=/opt/x-tools/$XTOOLS_HOST/$XTOOLS_HOST/sysroot" >> $GITHUB_ENV
      - name: Prepare environment for installing Qt
        if: matrix.arch == 'x86_64'
        run: |
          apt-get update
          apt-get install -y sudo
          echo "PIP_BREAK_SYSTEM_PACKAGES=1" >> $GITHUB_ENV
      - name: Install Qt
        if: matrix.arch == 'x86_64'
        uses: jurplel/install-qt-action@v4
        with:
          version: '6.7.0'
          cache: true
          setup-python: false
      - name: Configure
        run: >-
          ./configure
          "--prefix=$FRIDA_PREFIX"
          --host=$XTOOLS_HOST
          --enable-gadget
          --enable-server
          --enable-portal
          --enable-inject
          --enable-frida-python
          --
          -Dlibdir=lib
          -Dfrida_qml=${{ matrix.arch == 'x86_64' && 'enabled' || 'disabled' }}
          -Dfrida-gum:devkits=gum,gumjs
          -Dfrida-core:compiler_backend=${{ !(startsWith(matrix.arch, 'mips') || matrix.arch == 'armbe8' || matrix.arch == 'arm64be') && 'enabled' || 'disabled' }}
          -Dfrida-core:devkits=core
      - name: Compile
        run: make
      - name: Install
        run: make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/core/
      - name: Upload frida-server
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-server
      - name: Upload frida-portal
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-portal
      - name: Upload frida-inject
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-inject
      - name: Upload 32-bit frida-gadget
        if: ${{ !contains(matrix.arch, '64') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/32/frida-gadget.so
      - name: Upload 64-bit frida-gadget
        if: ${{ contains(matrix.arch, '64') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/64/frida-gadget.so
      - name: Package Python bindings
        run: |
          export FRIDA_EXTENSION=$(find "$FRIDA_PREFIX" -name _frida.abi3.so)
          case ${{ matrix.arch }} in
            *-musl)
              py_oses=(musllinux_1_1)
              ;;
            x86*)
              py_oses=(manylinux_2_5 manylinux1)
              ;;
            arm*|ppc*|s390x)
              py_oses=(manylinux_2_17 manylinux2014)
              ;;
            *)
              py_oses=(manylinux_2_5)
              ;;
          esac
          frida_arch=$(echo "${{ matrix.arch }}" | cut -f1 -d"-")
          case $frida_arch in
            x86)
              py_arch=i686
              ;;
            armhf)
              py_arch=armv7l
              ;;
            arm64)
              py_arch=aarch64
              ;;
            *)
              py_arch=$frida_arch
              ;;
          esac
          cd subprojects/frida-python
          export _PYTHON_HOST_PLATFORM=linux-$py_arch
          for py_os in "${py_oses[@]}"; do
            echo "plat_name = ${py_os}_${py_arch}" >> setup.cfg
            pip wheel -w "$FRIDA_PREFIX/wheels" --no-deps .
            git checkout setup.cfg
          done
        shell: bash
      - name: Upload Python bindings
        uses: actions/upload-artifact@v4
        with:
          name: frida-python-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/wheels/*.whl
      - name: Build Node.js bindings
        if: ${{ !(startsWith(matrix.arch, 'mips') || endsWith(matrix.arch, '-musl') || matrix.arch == 'armbe8' || matrix.arch == 'arm64be') }}
        run: |
          installdir=$FRIDA_PREFIX/prebuilds
          export FRIDA_DEPS=$PWD/deps
          export PKG_CONFIG_PATH=$FRIDA_PREFIX/lib/pkgconfig
          python tools/ensure-submodules.py frida-node
          cd subprojects/frida-node
          npm version $FRIDA_VERSION
          ./configure \
              "--prefix=$FRIDA_PREFIX" \
              --host=$XTOOLS_HOST
          make prebuild
          mkdir "$installdir"
          cp build/*.tar.gz "$installdir"
        shell: bash
      - name: Upload Node.js bindings
        if: ${{ !(startsWith(matrix.arch, 'mips') || endsWith(matrix.arch, '-musl') || matrix.arch == 'armbe8' || matrix.arch == 'arm64be') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-node-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/prebuilds/*.tar.gz
      - name: Upload QML bindings
        if: matrix.arch == 'x86_64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-qml-linux-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/qml/

  frida-ios:
    needs: [sdk-macos, sdk-ios]
    strategy:
      matrix:
        arch: [arm64, arm64e, x86_64-simulator, arm64-simulator]
      fail-fast: false
    runs-on: macos-latest
    env:
      XCODE11: /Applications/Xcode_11.7.app
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Grab frida-core
        run: python tools/ensure-submodules.py frida-core
      - name: Install Go toolchain
        uses: actions/setup-go@v5
        with:
          go-version-file: ${{ env.FRIDA_COMPILER_MOD }}
          cache-dependency-path: ${{ env.FRIDA_COMPILER_MOD }}
      - name: Configure
        run: >-
          ./configure
          --prefix=/usr
          --host=ios-${{ matrix.arch }}
          --enable-portal
          --
          -Dfrida-gum:devkits=gum,gumjs
          -Dfrida-core:compiler_backend=${{ matrix.arch != 'arm64e' && 'enabled' || 'disabled' }}
          -Dfrida-core:assets=installed
          -Dfrida-core:devkits=core
      - name: Compile
        run: make
      - name: Install
        run: DESTDIR="$FRIDA_PREFIX" make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/devkits/core/
      - name: Upload frida-server
        if: ${{ matrix.arch != 'arm64' && !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/bin/frida-server
      - name: Upload frida-portal
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/bin/frida-portal
      - name: Upload frida-inject
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/bin/frida-inject
      - name: Upload frida-helper
        if: ${{ matrix.arch != 'arm64' && !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-helper-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/frida-helper
      - name: Upload frida-agent
        if: ${{ matrix.arch != 'arm64' && !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-agent-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/frida-agent.dylib
      - name: Upload frida-gadget
        if: matrix.arch != 'arm64'
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-ios-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/frida-gadget.dylib

  frida-watchos:
    needs: [sdk-macos, sdk-watchos]
    strategy:
      matrix:
        arch: [arm64, arm64-simulator]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Configure
        run: >-
          ./configure
          "--prefix=$FRIDA_PREFIX"
          --host=watchos-${{ matrix.arch }}
          --
          -Dfrida-gum:devkits=gum,gumjs
          -Dfrida-core:devkits=core
      - name: Compile
        run: make
      - name: Install
        run: make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-watchos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-watchos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-watchos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/core/
      - name: Upload frida-gadget
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-watchos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/frida-gadget.dylib

  frida-tvos:
    needs: [sdk-macos, sdk-tvos]
    strategy:
      matrix:
        arch: [arm64, arm64-simulator]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Grab frida-core
        run: python tools/ensure-submodules.py frida-core
      - name: Configure
        run: >-
          ./configure
          --prefix=/usr
          --host=tvos-${{ matrix.arch }}
          --enable-portal
          --
          -Dfrida-gum:devkits=gum,gumjs
          -Dfrida-core:assets=installed
          -Dfrida-core:devkits=core
      - name: Compile
        run: make
      - name: Install
        run: DESTDIR="$FRIDA_PREFIX" make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/devkits/core/
      - name: Upload frida-server
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/bin/frida-server
      - name: Upload frida-portal
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/bin/frida-portal
      - name: Upload frida-inject
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/bin/frida-inject
      - name: Upload frida-helper
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-helper-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/frida-helper
      - name: Upload frida-agent
        if: ${{ !endsWith(matrix.arch, '-simulator') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-agent-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/frida-agent.dylib
      - name: Upload frida-gadget
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-tvos-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/usr/lib/frida/frida-gadget.dylib

  frida-android:
    needs: [sdk-linux, sdk-android-32, sdk-android-64]
    strategy:
      matrix:
        arch: [x86, x86_64, arm, arm64]
      fail-fast: false
    runs-on: ubuntu-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Grab frida-core
        run: python tools/ensure-submodules.py frida-core
      - name: Install Go toolchain
        uses: actions/setup-go@v5
        with:
          go-version-file: ${{ env.FRIDA_COMPILER_MOD }}
          cache-dependency-path: ${{ env.FRIDA_COMPILER_MOD }}
      - name: Set up NDK
        id: setup-ndk
        uses: nttld/setup-ndk@v1
        with:
          ndk-version: ${{ env.ANDROID_NDK_VERSION }}
          add-to-path: false
      - name: Add ANDROID_NDK_ROOT to environment
        run: echo "ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }}" >> $GITHUB_ENV
      - name: Configure
        run: >-
          ./configure
          "--prefix=$FRIDA_PREFIX"
          --host=android-${{ matrix.arch }}
          --enable-portal
          --
          -Dfrida-gum:devkits=gum,gumjs
          -Dfrida-core:compiler_backend=enabled
          -Dfrida-core:devkits=core
      - name: Compile
        run: make
      - name: Install
        run: make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/core/
      - name: Upload frida-server
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-server
      - name: Upload frida-portal
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-portal
      - name: Upload frida-inject
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-inject
      - name: Upload 32-bit frida-gadget
        if: ${{ !contains(matrix.arch, '64') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/32/frida-gadget.so
      - name: Upload 64-bit frida-gadget
        if: ${{ contains(matrix.arch, '64') }}
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-android-${{ matrix.arch }}
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/64/frida-gadget.so

  frida-qnx:
    runs-on: ubuntu-latest
    needs: sdk-linux
    container: ghcr.io/frida/qnx-tools:latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Add CFLAGS to environment
        run: echo "CFLAGS=\"--sysroot=$QNX_TARGET/armle-v7\"" >> $GITHUB_ENV
      - name: Roll SDK
        run: releng/deps.py roll sdk arm-unknown-nto-qnx6.5.0eabi
      - name: Configure
        run: >-
          ./configure
          "--prefix=$FRIDA_PREFIX"
          --host=arm-unknown-nto-qnx6.5.0eabi
          --enable-portal
          --
          -Dfrida-gum:devkits=gum,gumjs
          -Dfrida-core:devkits=core
      - name: Compile
        run: make
      - name: Install
        run: make install
      - name: Upload Gum devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gum-devkit-qnx-armeabi
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gum/
      - name: Upload GumJS devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-gumjs-devkit-qnx-armeabi
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/gumjs/
      - name: Upload Core devkit
        uses: actions/upload-artifact@v4
        with:
          name: frida-core-devkit-qnx-armeabi
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/devkits/core/
      - name: Upload frida-server
        uses: actions/upload-artifact@v4
        with:
          name: frida-server-qnx-armeabi
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-server
      - name: Upload frida-portal
        uses: actions/upload-artifact@v4
        with:
          name: frida-portal-qnx-armeabi
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-portal
      - name: Upload frida-inject
        uses: actions/upload-artifact@v4
        with:
          name: frida-inject-qnx-armeabi
          path: ${{ env.FRIDA_PREFIX }}/bin/frida-inject
      - name: Upload frida-gadget
        uses: actions/upload-artifact@v4
        with:
          name: frida-gadget-qnx-armeabi
          path: ${{ env.FRIDA_PREFIX }}/lib/frida/32/frida-gadget.so

  toolchain-windows:
    strategy:
      matrix:
        arch: [x86, arm64]
      fail-fast: false
    runs-on: ${{ matrix.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }}
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-windows-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll toolchain
        run: python releng\deps.py roll toolchain windows-${{ matrix.arch }}

  sdk-windows:
    needs: toolchain-windows
    strategy:
      matrix:
        arch: [x86, x86_64, arm64]
        config: [md, mdd, mt, mtd]
      fail-fast: false
    runs-on: ${{ matrix.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }}
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-windows-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll SDK
        run: python releng\deps.py roll sdk windows-${{ matrix.arch }}-${{ matrix.config }} --activate

  toolchain-macos:
    strategy:
      matrix:
        arch: [x86_64, arm64]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll toolchain
        run: releng/deps.py roll toolchain macos-${{ matrix.arch }}

  sdk-macos:
    needs: toolchain-macos
    strategy:
      matrix:
        arch: [x86_64, arm64, arm64e]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll SDK
        run: releng/deps.py roll sdk macos-${{ matrix.arch }} --activate

  sdk-ios:
    needs: toolchain-macos
    strategy:
      matrix:
        arch: [arm64, arm64e, x86_64-simulator, arm64-simulator]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll SDK
        run: releng/deps.py roll sdk ios-${{ matrix.arch }} --activate

  sdk-watchos:
    needs: toolchain-macos
    strategy:
      matrix:
        arch: [arm64, arm64-simulator]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll SDK
        run: releng/deps.py roll sdk watchos-${{ matrix.arch }} --activate

  sdk-tvos:
    needs: toolchain-macos
    strategy:
      matrix:
        arch: [arm64, arm64-simulator]
      fail-fast: false
    runs-on: macos-latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-macos-env
        with:
          certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
          certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll SDK
        run: releng/deps.py roll sdk tvos-${{ matrix.arch }} --activate

  toolchain-linux:
    strategy:
      matrix:
        arch: [x86, x86_64, x86_64-musl, armhf, armhf-musl, armbe8, arm64, arm64be, arm64beilp32, arm64-musl]
      fail-fast: false
    runs-on: ubuntu-latest
    container: ghcr.io/frida/x-tools-linux-${{ matrix.arch }}:latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Roll toolchain
        run: releng/deps.py roll toolchain $XTOOLS_HOST

  sdk-linux:
    needs: toolchain-linux
    strategy:
      matrix:
        arch: [x86, x86_64, x86_64-musl, armhf, armhf-musl, armbe8, arm64, arm64be, arm64beilp32, arm64-musl, mips, mipsel, mips64, mips64el]
      fail-fast: false
    runs-on: ubuntu-latest
    container: ghcr.io/frida/x-tools-linux-${{ matrix.arch }}:latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Set FRIDA_CAN_RUN_HOST_BINARIES
        if: ${{ matrix.arch == 'x86' }}
        run: echo "FRIDA_CAN_RUN_HOST_BINARIES=yes" >> $GITHUB_ENV
      - name: Configure qemu-user
        if: ${{ !startsWith(matrix.arch, 'x86') }}
        run: echo "FRIDA_QEMU_SYSROOT=/opt/x-tools/$XTOOLS_HOST/$XTOOLS_HOST/sysroot" >> $GITHUB_ENV
      - name: Roll SDK
        run: releng/deps.py roll sdk $XTOOLS_HOST --activate

  sdk-android-32:
    needs: toolchain-linux
    strategy:
      matrix:
        arch: [x86, arm]
      fail-fast: false
    runs-on: ubuntu-latest
    container: ghcr.io/frida/x-tools-linux-x86:latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Set up NDK
        id: setup-ndk
        uses: nttld/setup-ndk@v1
        with:
          ndk-version: ${{ env.ANDROID_NDK_VERSION }}
          add-to-path: false
      - name: Roll SDK
        run: releng/deps.py roll sdk android-${{ matrix.arch }} --build=$XTOOLS_HOST --activate
        env:
          ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}

  sdk-android-64:
    needs: toolchain-linux
    strategy:
      matrix:
        arch: [x86_64, arm64]
      fail-fast: false
    runs-on: ubuntu-latest
    container: ghcr.io/frida/x-tools-linux-x86_64:latest
    steps:
      - name: Check out repo
        uses: actions/checkout@v4
      - name: Set up environment
        uses: ./.github/actions/setup-linux-env
        with:
          aws-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
          cloudflare-email: ${{ secrets.CF_EMAIL }}
          cloudflare-token: ${{ secrets.CF_TOKEN }}
      - name: Set up NDK
        id: setup-ndk
        uses: nttld/setup-ndk@v1
        with:
          ndk-version: ${{ env.ANDROID_NDK_VERSION }}
          add-to-path: false
      - name: Roll SDK
        run: releng/deps.py roll sdk android-${{ matrix.arch }} --build=$XTOOLS_HOST --activate
        env:
          ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}


================================================
FILE: .gitignore
================================================
/build/
/deps/
/releng/__pycache__/
/releng/modules/frida-gadget-ios/*.dylib
/releng/modules/frida-gadget-ios/node_modules
/subprojects/*


================================================
FILE: .gitmodules
================================================
[submodule "frida-gum"]
	path = subprojects/frida-gum
	url = https://github.com/frida/frida-gum.git
[submodule "frida-core"]
	path = subprojects/frida-core
	url = https://github.com/frida/frida-core.git
[submodule "frida-python"]
	path = subprojects/frida-python
	url = https://github.com/frida/frida-python.git
[submodule "frida-node"]
	path = subprojects/frida-node
	url = https://github.com/frida/frida-node.git
[submodule "frida-swift"]
	path = subprojects/frida-swift
	url = https://github.com/frida/frida-swift.git
[submodule "frida-clr"]
	path = subprojects/frida-clr
	url = https://github.com/frida/frida-clr.git
[submodule "frida-qml"]
	path = subprojects/frida-qml
	url = https://github.com/frida/frida-qml.git
[submodule "frida-tools"]
	path = subprojects/frida-tools
	url = https://github.com/frida/frida-tools.git
[submodule "frida-go"]
	path = subprojects/frida-go
	url = https://github.com/frida/frida-go
[submodule "releng"]
	path = releng
	url = https://github.com/frida/releng.git


================================================
FILE: BSDmakefile
================================================
all: .DEFAULT

.DEFAULT:
	@gmake ${.MAKEFLAGS} ${.TARGETS}

.PHONY: all


================================================
FILE: CONTRIBUTING.md
================================================
# Intro

Frida is composed of many sub-projects and its code spans across many
different languages, such as C, C++, Vala, JavaScript, TypeScript,
Python, assembly, etc., and the coding conventions may vary across any
combination of project/language.

The rules presented here are not there because of @oleavr's OCD: they're
about keeping the codebase readable and maintainable, and give the code
a consistent structure as it grows. That helps every contributor to
easily orientate in it.

Moreover, reading this doc helps save everyone's time when it comes to
get a PR reviewed.

# General rules

Rules in this section apply in all cases, regardless of the programming
language or the project owning the code. Examples are in
pseudo-javascript for brevity (with some exceptions).

## Comments vs. naming

### Rule

Only use comments for communicating what cannot be conveyed by the code
itself through meaningful variable and function names, and splitting out
logic into separate functions.

**NOTE**: names should be long enough to be meaningful, but not 
ridiculously long.

#### Wrong

```c
// Init the dyld start address with current program counter
a = (instance->cpu_type == GUM_CPU_ARM64) ? __darwin_arm_thread_state64_get_pc (state.ts_64) : state.ts_32.__pc;
// dyld header is initially zero
b = 0;
// set the search granularity to 4k
c = 4096;

for (dyld_chunk = (a & (c - 1)) == 0 ? (a - c) : (a & ~(c - 1));
    b == 0;
    dyld_chunk -= c)
{
	...
}
```

#### Correct

```c
dyld_start = (instance->cpu_type == GUM_CPU_ARM64) ? __darwin_arm_thread_state64_get_pc (state.ts_64) : state.ts_32.__pc;
dyld_header = 0;
dyld_granularity = 4096;

for (dyld_chunk = (dyld_start & (dyld_granularity - 1)) == 0 ? (dyld_start - dyld_granularity) : (dyld_start & ~(dyld_granularity - 1));
    dyld_header == 0;
    dyld_chunk -= dyld_granularity)
{
	...
}
```

#### Example

Here's a **really useful** comment.

```vala
/* Compiled from helpers/upload-listener.c */
private const uint8[] UPLOAD_LISTENER_CODE = {
	0xff, 0x43, 0x01, 0xd1, 0xf6, 0x57, 0x02, 0xa9, 0xf4, 0x4f, 0x03, 0xa9, 0xfd, 0x7b, 0x04, 0xa9, 0xfd, 0x03, 0x01,
	0x91, 0xf3, 0x03, 0x01, 0xaa, 0xe0, 0x1f, 0x00, 0xb9, 0x28, 0x00, 0x40, 0xf9, 0xe0, 0x03, 0x1f, 0x32, 0xe1, 0x03,
	0x00, 0x32, 0x02, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x80, 0x05, 0x00, 0x54, 0xf4,
	...
	0xb2, 0x04, 0x00, 0x00, 0x14, 0xf5, 0x03, 0x46, 0xb2, 0x02, 0x00, 0x00, 0x14, 0x15, 0xa0, 0xe0, 0xd2, 0x68, 0x2a,
	0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x15, 0xaa, 0xfd, 0x7b, 0x44, 0xa9, 0xf4,
	0x4f, 0x43, 0xa9, 0xf6, 0x57, 0x42, 0xa9, 0xff, 0x43, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6
};
```

### Rule

The variable name shouldn't be more verbose than its type name.

#### Wrong

```vala
private void schedule_idle (owned ScheduledFunc function) { ... }
```

#### Correct

```vala
private void schedule_idle (owned ScheduledFunc func) { ... }
```

## Order of functions in a file

### Rule 

Higher level functions must be placed before lower level functions, and
Should be sorted chronologically.
In case of C code the corresponding forward declarations must follow
the same order.

#### Wrong

```javascript
function getInfoAboutA() {
    /* complex code */
    return allInfoAboutA;
}

function getInfoAboutB() {
    /* more complex code */
    return allInfoAboutB;
}

function getInfo() {
    return {
        infoAboutA: getInfoAboutA(),
        infoAboutB: getInfoAboutB()
    };
}
```

#### Correct

```javascript
function getInfo() {
    return {
        infoAboutA: getInfoAboutA(),
        infoAboutB: getInfoAboutB()
    };
}

function getInfoAboutA() {
    /* complex code */
    return allInfoAboutA;
}

function getInfoAboutB() {
    /* more complex code */
    return allInfoAboutB;
}
```

### Rule

For exported functions / public methods: 
functions that work in all cases come before functions which work 
only under certain conditions.

#### Wrong

```javascript
function getPrivilegedInfo() {
    if (!amIRoot()) {
        throw new Error("You must be root");
    }
    ...
    return privilegedInfo;
}

function getHarmlessInfo() {
    return harmlessInfo;
}
```

#### Correct

```javascript
function getHarmlessInfo() {
    return harmlessInfo;
}

function getPrivilegedInfo() {
    if (!amIRoot()) {
        throw new Error("You must be root");
    }
    ...
    return privilegedInfo;
}
```

## Don't repeat yourself

### Rule

Instead of repeating chunks of code, extract it to a function and 
call it multiple times.

#### Example

There's no fixed recipe to define what a "repeated chunk of code is", it
really depends on the context. For example, even a couple of lines may
be worth refactoring into a function, especially if they require some
fairly complex error-handling logic.

```vala
private async LLDB.Client start_lldb_service (Fruity.LockdownClient lockdown, Cancellable? cancellable)
		throws Error, LLDB.Error, IOError {
	try {
		var lldb_stream = yield lockdown.start_service (DEBUGSERVER_SERVICE_NAME + "?tls=handshake-only", cancellable);
		return yield LLDB.Client.open (lldb_stream, cancellable);
	} catch (Fruity.LockdownError e) {
		if (e is Fruity.LockdownError.INVALID_SERVICE) {
			throw new Error.NOT_SUPPORTED ("This feature requires an iOS Developer Disk Image to be mounted; " +
				"run Xcode briefly or use ideviceimagemounter to mount one manually");
		}
		throw new Error.NOT_SUPPORTED ("%s", e.message);
	}
}
```

## Indentation

### Rule

Minimize nesting when possible.

#### Wrong

```javascript
function doSomethingMaybe() {
    if (condition) {
        /* do something here */
    }
}
```

#### Correct

```javascript
function doSomethingMaybe() {
    if (!condition) {
        return;
    }
    /* do something here */
}
```

### Rule

Hanging indent – when breaking a long line of code – should be *twice*
the regular indent, regardless of the indentation rules.

#### Wrong

```c
recursive_init_address = gum_darwin_module_resolve_symbol_address (dyld,
  "__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE");
```

#### Correct

```c
recursive_init_address = gum_darwin_module_resolve_symbol_address (dyld,
    "__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEjRNS_21InitializerTimingListERNS_15UninitedUpwardsE");
```

## Spaces

### Rule

No trailing spaces.

## Indexed loops

### Rule

Prefer strict inequality for loop conditions.

#### Wrong

```javascript
for (let i = 0; i < len; i++) {
  ...
}
```

#### Correct

```javascript
for (let i = 0; i !== len; i++) {
  ...
}
```

## Comparisons

### Rule

Use explicit comparisons, even with `0`, `NULL`, and `undefined`,
instead of relying on language-specific coercion. Only use implicit
for booleans.

#### Wrong

```javascript
if (value) {
  ...
}
```

#### Correct

```javascript
if (value !== 0) {
  ...
}
```

## Argument validation

### Rule

Internal APIs should assume the API contract is not violated – i.e.
that the function is passed everything it needs – and omit any check
on the arguments.

#### Wrong

```javascript
function doSomethingInternallyWithDevice(device) {
  if (device === null || device === undefined) {
     throw new Error('Pass a valid device');
  }
  
  /* do the thing with device */
}
```

#### Correct

```javascript
function doSomethingInternallyWithDevice(device) {
  /* do the thing with device */
}
```

# Rules for C

This set of rules applies to C code, regardless of the project owning it.

## File structure

### Rule

Every C file must follow the following structure, in this order:

* Own header includes
* Internal header includes
* System/dependency includes
* typedefs
* enum and struct definitions
* Forward declarations / prototypes
* Global state / static variables
* Implementation code

Each such group should be separated by a blank line, and includes should
be listed in alphabetical order.

## Spaces

### Rule

In function calls and function definitions, put a space before
the parenthesis.

#### Wrong

```c
ret = thread_create(task, &instance->thread);
```

#### Correct

```c
ret = thread_create (task, &instance->thread);
```

### Rule

When declaring pointers, put a space before and after `*`.

#### Wrong

```c
GumModuleDetails *details;
```

#### Correct

```c
GumModuleDetails * details;
```

### Rule

When casting pointers, put a space before `*`.

#### Wrong

```c
init_func = (guint32*) gum_darwin_read (task, addr, sizeof (guint32), NULL);
```

#### Correct

```c
init_func = (guint32 *) gum_darwin_read (task, addr, sizeof (guint32), NULL);
```

### Rule

Space before and after binary operators, no spaces around unary
operators.

#### Wrong

```c
for (port_index=0; port_index!=previous_ports->count; port_index ++)
{
  ...
}

* count ++;
```

#### Correct

```c
for (port_index = 0; port_index != previous_ports->count; port_index++)
{
  ...
}

*count++;
```

### Rule

Never use more than one space (when not used for indentation).

#### Wrong

```c
gint  i;
```

#### Correct

```c
gint i;
```

## Blank Lines

### Rule

It's encouraged to leave **one** blank line when needing to separate
semantically distinct blocks of code or improve readability
(some examples later). Just don't abuse that and never leave more 
than one blank line. (Except for Python code, which should follow the
recommendations in PEP-8.)

#### Wrong

```
static void
function_one (void)
{
  ...
}


static void
function_two (void)
{
  ...
}
```

#### Correct

```
static void
function_one (void)
{
  ...
}

static void
function_two (void)
{
  ...
}
```

## Function definitions

### Rule

Function names must be lowercase, e.g. `find_libsystem`
**NOTE**: this is true also for arguments, variable names, and
labels.

#### Wrong

```c
static gboolean
frida_find_libSystem (const GumModuleDetails * details, gpointer user_data)
{
    ...
}
```

#### Correct

```c
static gboolean
frida_find_libsystem (const GumModuleDetails * details, gpointer user_data)
{
    ...
}
```

### Rule

Functions which take no arguments should be declared `(void)`, as `()`
means that no information about the number or types of the arguments
is supplied. (Unlike C++, where `()` means "no arguments".)

#### Wrong

```c
static gboolean 
function_without_args ()
{
  ...
}
```

#### Correct

```c
static gboolean 
function_without_args (void)
{
  ...
}
```

## Local variables

### Rule

Local variables are all declared at the beginning of the block
they're used in, a blank line is usually left right after.

#### Wrong

```c
  if (is_uninitialized_clone)
  {
    mach_port_mod_refs (self_task, task, MACH_PORT_RIGHT_SEND, 1);
    instance->task = task;

    mach_vm_address_t data_address = instance->remote_agent_context;
    ...
  }
```

#### Correct

```c
  if (is_uninitialized_clone)
  {
    mach_vm_address_t data_address;

    mach_port_mod_refs (self_task, task, MACH_PORT_RIGHT_SEND, 1);
    instance->task = task;

    data_address = instance->remote_agent_context;
    ...
  }
```

### Rule

Local variables are listed in chronological order of usage.

**Exception 1**: the variable which holds the return value of a
function should be the first in the list, preceded by any variable
holding argument values.

**Exception 2**: related variables of the same type can be grouped in
one line, in relative chronological order.

#### Example 1

Variables in chronological order of usage.

```c
    case FRIDA_BREAKPOINT_CLEANUP:
    {
      task_t self_task;
      gsize page_size;
      FridaExceptionPortSet * previous_ports;
      mach_msg_type_number_t port_index;
      guint i;

      self_task = mach_task_self ();
      page_size = getpagesize ();

      previous_ports = &self->previous_ports;
      for (port_index = 0; port_index != previous_ports->count; port_index++)
      {
        ...
      }
      
      ...

      for (i = 0; i != FRIDA_MAX_BREAKPOINTS; i++)
        frida_spawn_instance_unset_nth_breakpoint (self, i);
    }
```

#### Example 2

The variable holding the return value comes first.

```c
static csh
frida_create_capstone (GumCpuType cpu_type, GumAddress start)
{
  csh capstone;
  cs_err err;

  switch (cpu_type)
  {
    case GUM_CPU_ARM64:
      err = cs_open (CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN, &capstone);
      break;
    ...
  }

  g_assert (err == CS_ERR_OK);

  return capstone;
}
```

#### Example 3

Argument helper variable before return value.

```c
guint
_frida_darwin_helper_backend_demonitor_and_clone_injectee_state (FridaDarwinHelperBackend * self, void * raw_instance)
{
  FridaInjectInstance * instance = raw_instance;
  FridaInjectInstance * clone;

  ...

  return clone->id;
}
```

### Rule

Avoid redundant initialization.

#### Wrong

```c
cs_insn * insn = NULL;

...

insn = cs_malloc (capstone);
```

#### Correct

```c
cs_insn * insn;

...

insn = cs_malloc (capstone);
```

## Indentation and {}

### Rule

In function definitions, the return type goes on its own line (not
for the prototype).

#### Wrong

```c
static gboolean frida_find_libsystem (const GumModuleDetails * details, gpointer user_data)
{
  ...
}
```

#### Correct

```c
static gboolean
frida_find_libsystem (const GumModuleDetails * details, gpointer user_data)
{
  ...
}
```

### Rule

Curly braces go on a new line, in all cases.

#### Wrong

```c
if (cached_address_for_breakpoint[instance->cpu_type] == 0) {
  ...
}
```

#### Correct

```c
if (cached_address_for_breakpoint[instance->cpu_type] == 0) 
{
  ...
}
```

### Rule

Code in a block is indented by 2 spaces.

#### Wrong

```c
if (magic == NULL)
    goto handle_probe_dyld_error;
```

#### Correct

```c
if (magic == NULL)
  goto handle_probe_dyld_error;
```

### Rule

Simple `if` statements in which the code is only one line should not
have curly braces.

#### Wrong

```c
if (magic == NULL)
{
  goto handle_probe_dyld_error;
}
```

#### Correct

```c
if (magic == NULL)
  goto handle_probe_dyld_error;
```

### Rule

If the `if` statement has curly braces, then also the `else` should
have it (and vice-versa).

#### Wrong

```c
    if (error == nil)
      [service cleanupClientPort:client_port];
    else
    {
      g_clear_object (&pipes);

      frida_error = g_error_new (
          FRIDA_ERROR,
          FRIDA_ERROR_NOT_SUPPORTED,
          "Unable to launch iOS app: %s",
          [[error localizedDescription] UTF8String]);
    }
```

#### Correct

```c
    if (error == nil)
    {
      [service cleanupClientPort:client_port];
    }
    else
    {
      g_clear_object (&pipes);

      frida_error = g_error_new (
          FRIDA_ERROR,
          FRIDA_ERROR_NOT_SUPPORTED,
          "Unable to launch iOS app: %s",
          [[error localizedDescription] UTF8String]);
    }
```

### Rule

When an `if` statement has single-statement bodies but any of them
exceed the maximum line length, curly braces must be used.

#### Wrong

```c
  if (_frida_get_springboard_api ()->fbs != NULL)
    frida_darwin_helper_backend_launch_using_fbs (identifier_value, url_value, options, aux_options, on_complete,
        on_complete_target);
  else
    frida_darwin_helper_backend_launch_using_sbs (identifier_value, url_value, options, aux_options, on_complete,
        on_complete_target);
```

#### Correct

```c
  if (_frida_get_springboard_api ()->fbs != NULL)
  {
    frida_darwin_helper_backend_launch_using_fbs (identifier_value, url_value, options, aux_options, on_complete,
        on_complete_target);
  }
  else
  {
    frida_darwin_helper_backend_launch_using_sbs (identifier_value, url_value, options, aux_options, on_complete,
        on_complete_target);
  }
```

### Rule

If the condition of an `if` statement has been broken up into multiple
lines, then use braces regardless.

#### Wrong

```c
    if ((ctx->sink_mask & GUM_BLOCK) != 0 &&
        gum_x86_relocator_eob (rl) &&
        insn.ci->id != X86_INS_CALL)
      gum_exec_block_write_block_event_code (block, &gc, GUM_CODE_INTERRUPTIBLE);
```

#### Correct

```c
    if ((ctx->sink_mask & GUM_BLOCK) != 0 &&
        gum_x86_relocator_eob (rl) &&
        insn.ci->id != X86_INS_CALL)
    {
      gum_exec_block_write_block_event_code (block, &gc, GUM_CODE_INTERRUPTIBLE);
    }
```

## Switch statements

### Rule

If a case needs curly braces, the `break` goes inside, usually
preceded by a blank line.

#### Wrong

```c
switch (cpu_type)
{
  case GUM_CPU_ARM:
  {
    /* case implementation*/
  }
  break;
  
  ...
}
```

#### Correct

```c
switch (cpu_type)
{
  case GUM_CPU_ARM:
  {
    /* case implementation*/
    
    break;
  }
  
  ...
}
```

# Rules for JavaScript

This set of rules apply to JavaScript code, regardless of the project owning it.

## String constants

### Rule

Don't use double quotes.

#### Wrong

```javascript
throw new Error("Invalid argument");
```

#### Correct

```javascript
throw new Error('Invalid argument');
```

## Indentation

### Rule

Indent with 2 spaces.

### Rule

Curly braces go on the same line of the statement.

#### Wrong

```javascript
if (condition)
{
  ...
}
else
{
  ...
}
```

#### Correct

```javascript
if (condition) {
  ...
} else {
  ...
}
```

## Semicolons

### Rule

Treat semicolons as mandatory.

#### Wrong

```javascript
console.log('hello world')
```

#### Correct

```javascript
console.log('hello world');
```

## Comparisons

### Rule

Use strict comparisons.

#### Wrong

```javascript
if (methodName == '- init') {
  ...
}
```

#### Correct

```javascript
if (methodName === '- init') {
  ...
}
```

### Rule

Put parenthesis around the ternary comparison condition unless it's
simply referencing a boolean variable.

#### Wrong

```javascript
m = res[0] === '' ? '*' : res[0];
```

#### Correct

```javascript
m = (res[0] === '') ? '*' : res[0];
```

## Spaces

### Rule

Unless the specific project follows the semistandard conventions, put no
spaces between function name and argument list.

#### Wrong

```javascript
function parseExportsFunctionPattern (pattern) {
  var res = pattern.split ('!');
  ...
}
```

#### Correct

```javascript
function parseExportsFunctionPattern(pattern) {
  var res = pattern.split('!');
  ...
}
```

## Object properties

### Rule

Reference object properties without quotes when possible.

#### Wrong

```javascript
enumerateMatches('exports:' + obj['module'] + '!' + obj['function']);
```

#### Correct

```javascript
enumerateMatches('exports:' + obj.module + '!' + obj.function);
```

## Internal agents

### Rule

Stick to `ES5` syntax, so it can be consumed by the Duktape runtime
without having to first `frida-compile` the code.

### Rule

In `rpc.exports`, `dispose()` comes first, or right after `init()`.

# Rules for TypeScript

This set of rules apply to TypeScript code, regardless of the project
owning it. Rules for JavaScript also apply to TypeScript, if not
explicitly overridden.

## Indentation

### Rule

Indent with 4 spaces.

## Spaces

### Rule

Never put spaces between function name and argument list, neither in
calls nor definitions.

## String constants

### Rule

Don't use single quotes.

## Enums

### Rule

The TypeScript convention is pascal-case for enum values,
e.g. `FooBarBaz`.

#### Wrong

```typescript
enum PlistType {
    NONE,
    BINARY,
    XML
}
```

#### Correct

```typescript
enum PlistType {
    None,
    Binary,
    Xml
}
```

### Rule

Usage of `const enum` is discouraged portability-wise.

#### Wrong

```typescript
export const enum GrassColor {
    Yellow = "YELLOW",
    LightGreen = "LIGHTGREEN",
    Green = "GREEN",
    DarkGreen = "DARKGREEN"
}
```

#### Correct

```typescript
export type GrassColor = 
    | "YELLOW" 
    | "LIGHTGREEN" 
    | "GREEN" 
    | "DARKGREEN"
    ;
```

## Constants

### Rule

Constants should be uppercase, e.g. `FOO_BAR_BAZ`.

#### Wrong

```typescript
const lockdownPort = 62078;
```

#### Correct

```typescript
const LOCKDOWN_PORT = 62078;
```

## Types

### Rule

Use `interface` when possible.

#### Wrong

```typescript
export type StringDict = {
    [name: string]: string;
}
```

#### Correct

```typescript
export interface StringDict {
    [name: string]: string;
}
```

# Rules for Vala

This set of rules apply to Vala code, regardless of the project
owning it.

## Order of class properties

### Rule

* Public properties should precede private ones.
* Higher level properties should precede lower level ones.

## Indentation, spaces and {}

### Rule

Indent only with tabs.

### Rule

Rules for spaces are the same as C.

### Rule

Curly braces go on the same line of the statement. Follow the same
rules as C for whether to omit them or not.

#### Wrong

```vala
if (condition)
{
  ...
}
else
{
  ...
}
```

#### Correct

```vala
if (condition) {
  ...
} else {
  ...
}
```

## Usage of `var` type

### Rule

Declare variables as `var`, especially when the type is obvious.

#### Wrong

```vala
Json.Node parameters = new Json.Node (Json.NodeType.OBJECT);
```

#### Correct

```vala
var parameters = new Json.Node (Json.NodeType.OBJECT);
```

# Generic rules for Python

This set of rules apply to all python code, regardless of the project
it belongs to.

## Formatting

### Rule

Python formatting should follow the 
[PEP-8](https://www.python.org/dev/peps/pep-0008/) guidelines.

## String quotes

### Rule

Use double quotes for regular strings, single quotes for enum-like
values.

#### Wrong

```python
if os.environ.get("TERM", '') == "dumb":
```

#### Correct

```python
if os.environ.get("TERM", "") == 'dumb':
```

## Imports

### Rule

Imports go in alphabetical order.

## Comments

### Rule

Comments should use a capital letter at the start of each sentence,
and each should end in a full stop.

# Generic rules in frida-gum

This set of rules apply to all code in frida-gum, regardless of the
language.

## Max line length

### Rule

Lines should not exceed 80 characters.

# Rules for C code in frida-gum

## Function definitions

### Rule

When defining a function implementation each argument must go on
its own line, vertically aligned (not for the prototype).

#### Wrong

```c
static void
gum_exec_block_write_block_event_code (GumExecBlock * block, GumGeneratorContext * gc,
                                       GumCodeContext cc)
{
  ...
}
```

#### Correct

```c
static void
gum_exec_block_write_block_event_code (GumExecBlock * block,
                                       GumGeneratorContext * gc,
                                       GumCodeContext cc)
{
  ...
}
```

# Generic rules in frida-core

This set of rules apply to all code in frida-core, regardless of the
language.

## Max line length

### Rule

Lines should not exceed 140 characters.

# Rules for C code in frida-core

This set of rules apply to C code belonging to frida-core.

## Function definitions

### Rule

Function names should be “namespaced” by having a `frida_` prefix,
even if static.

#### Wrong

```c
static gboolean
find_libsystem (const GumModuleDetails * details, gpointer user_data)
{
  ...
}
```

#### Correct

```c
static gboolean
frida_find_libsystem (const GumModuleDetails * details, gpointer user_data)
{
  ...
}
```


================================================
FILE: COPYING
================================================
              wxWindows Library Licence, Version 3.1
              ======================================

Copyright (c) 1998-2005 Julian Smart, Robert Roebling et al

Everyone is permitted to copy and distribute verbatim copies
of this licence document, but changing it is not allowed.

                     WXWINDOWS LIBRARY LICENCE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public Licence as published by
the Free Software Foundation; either version 2 of the Licence, or (at your
option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
Licence for more details.

You should have received a copy of the GNU Library General Public Licence
along with this software, usually in a file named COPYING.LIB.  If not,
write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
Floor, Boston, MA 02110-1301 USA.

EXCEPTION NOTICE

1. As a special exception, the copyright holders of this library give
permission for additional uses of the text contained in this release of the
library as licenced under the wxWindows Library Licence, applying either
version 3.1 of the Licence, or (at your option) any later version of the
Licence as published by the copyright holders of version 3.1 of the Licence
document.

2. The exception is that you may use, copy, link, modify and distribute
under your own terms, binary object code versions of works based on the
Library.

3. If you copy code from files distributed under the terms of the GNU
General Public Licence or the GNU Library General Public Licence into a
copy of this library, as this licence permits, the exception does not apply
to the code that you add in this way.  To avoid misleading anyone as to the
status of such modified files, you must delete this exception notice from
such code and/or adjust the licensing conditions notice accordingly.

4. If you write modifications of your own for this library, it is your
choice whether to permit this exception to apply to your modifications.  If
you do not wish that, you must delete the exception notice from such code
and/or adjust the licensing conditions notice accordingly.


================================================
FILE: Makefile
================================================
PYTHON ?= $(shell which python3 >/dev/null && echo python3 || echo python)

all $(MAKECMDGOALS):
	@$(PYTHON) \
		-c "import sys; sys.path.insert(0, sys.argv[1]); from releng.meson_make import main; main()" \
		"$(shell pwd)" \
		./build \
		$(MAKECMDGOALS)

git-submodules:
	@[ ! -f releng/meson/meson.py ] && $(PYTHON) tools/ensure-submodules.py
-include git-submodules

.PHONY: all $(MAKECMDGOALS)


================================================
FILE: README.md
================================================
# Frida

Dynamic instrumentation toolkit for developers, reverse-engineers, and security
researchers. Learn more at [frida.re](https://frida.re/).

Two ways to install
===================

## 1. Install from prebuilt binaries

This is the recommended way to get started. All you need to do is:

    pip install frida-tools # CLI tools
    pip install frida       # Python bindings
    npm install frida       # Node.js bindings

You may also download pre-built binaries for various operating systems from
Frida's [releases](https://github.com/frida/frida/releases) page on GitHub.

## 2. Build your own binaries

Run:

    make

You may also invoke `./configure` first if you want to specify a `--prefix`, or
any other options.

### CLI tools

For running the Frida CLI tools, e.g. `frida`, `frida-ls-devices`, `frida-ps`,
`frida-kill`, `frida-trace`, `frida-discover`, etc., you need a few packages:

    pip install colorama prompt-toolkit pygments

### Apple OSes

First make a trusted code-signing certificate. If you have already used Xcode
before, chances are you already have an Apple development certificate.
You can check it with the following command:

    security find-identity -v -p codesigning

Which will return the certificate in the following format:

    1) XXXXX "Apple Development: user@mail.com (XXXXX)"

If you do not have a certificate, follow this guide: 
https://help.apple.com/xcode/mac/current/#/dev154b28f09.

Next export the name of your certificate to relevant environment
variables, and run `make`:

    export MACOS_CERTID="Apple Development: user@mail.com (XXXXXXXXXX)"
    export IOS_CERTID="Apple Development: user@mail.com (XXXXXXXXXX)"
    export WATCHOS_CERTID="Apple Development: user@mail.com (XXXXXXXXXX)"
    export TVOS_CERTID="Apple Development: user@mail.com (XXXXXXXXXX)"
    make

## Learn more

Have a look at our [documentation](https://frida.re/docs/home/).


================================================
FILE: configure
================================================
#!/bin/sh

[ -z "$PYTHON" ] && PYTHON=$(which python3 >/dev/null && echo python3 || echo python)

cd $(dirname $0)
srcroot=$(pwd)
[ ! -f releng/meson/meson.py ] && "$PYTHON" tools/ensure-submodules.py
cd - >/dev/null

exec "$PYTHON" \
    -c "import sys; sys.path.insert(0, sys.argv[1]); from releng.meson_configure import main; main()" \
    "$srcroot" \
    "$@"


================================================
FILE: configure.bat
================================================
@setlocal
@echo off
rem:: Based on: https://github.com/microsoft/terminal/issues/217#issuecomment-737594785
goto :_start_

:set_real_dp0
set dp0=%~dp0
set "dp0=%dp0:~0,-1%"
goto :eof

:_start_
call :set_real_dp0

if not exist "%dp0%\releng\meson\meson.py" (
  python "%dp0%\tools\ensure-submodules.py"
  if %errorlevel% neq 0 exit /b %errorlevel%
)

endlocal & goto #_undefined_# 2>nul || title %COMSPEC% & python ^
    -c "import sys; sys.path.insert(0, sys.argv[1]); from releng.meson_configure import main; main()" ^
    "%dp0%" ^
    %*


================================================
FILE: make.bat
================================================
@setlocal
@echo off
rem:: Based on: https://github.com/microsoft/terminal/issues/217#issuecomment-737594785
goto :_start_

:set_real_dp0
set dp0=%~dp0
set "dp0=%dp0:~0,-1%"
goto :eof

:_start_
call :set_real_dp0

if not exist "%dp0%\releng\meson\meson.py" (
  python "%dp0%\tools\ensure-submodules.py"
  if %errorlevel% neq 0 exit /b %errorlevel%
)

endlocal & goto #_undefined_# 2>nul || title %COMSPEC% & python ^
    -c "import sys; sys.path.insert(0, sys.argv[1]); from releng.meson_make import main; main()" ^
    "%dp0%" ^
    .\build ^
    %*


================================================
FILE: meson.build
================================================
project('frida', 'c',
  version: run_command('releng' / 'frida_version.py', check: true).stdout().strip(),
  meson_version: '>=1.1.0',
)

python = import('python').find_installation()

is_cross_build = meson.is_cross_build()
is_watchos = host_machine.subsystem().split('-')[0] == 'watchos'

gum_options = [
  'frida_version=' + meson.project_version(),
  'graft_tool=' + (get_option('graft_tool').disable_auto_if(is_cross_build).allowed() ? 'enabled' : 'disabled'),
  'gumjs=enabled',
]
subproject('frida-gum', default_options: gum_options)

core_options = [
  'frida_version=' + meson.project_version(),
]
core_options += 'gadget=' + (get_option('gadget').disable_auto_if(not is_cross_build).allowed() ? 'enabled' : 'disabled')
foreach component : ['server', 'portal', 'inject']
  core_options += component + '=' + (get_option(component)
    .disable_auto_if(not is_cross_build)
    .disable_auto_if(is_watchos)
    .allowed() ? 'enabled' : 'disabled')
endforeach
subproject('frida-core', default_options: core_options)

ensure_submodules = [python, files('tools' / 'ensure-submodules.py')]

if get_option('frida_clr') \
    .disable_auto_if(is_cross_build) \
    .disable_auto_if(build_machine.system() != 'windows') \
    .disable_auto_if(get_option('b_vscrt').startswith('mt')) \
    .allowed()
  run_command(ensure_submodules, 'frida-clr', check: true)
  if get_option('frida_clr').auto()
    detect_result = run_command(python, 'subprojects' / 'frida-clr' / 'detect-netfx.py', check: false)
    build_clr_bindings = detect_result.returncode() == 0
  else
    build_clr_bindings = true
  endif
  if build_clr_bindings
    subproject('frida-clr')
  endif
endif

if get_option('frida_node') \
    .disable_auto_if(is_cross_build) \
    .allowed()
  run_command(ensure_submodules, 'frida-node', check: true)
  subproject('frida-node')
endif

if get_option('frida_python') \
    .disable_auto_if(is_cross_build) \
    .allowed()
  run_command(ensure_submodules, 'frida-python', check: true)
  subproject('frida-python')
endif

if get_option('frida_swift') \
    .disable_auto_if(is_cross_build) \
    .disable_auto_if(build_machine.system() != 'macos') \
    .allowed()
  run_command(ensure_submodules, 'frida-swift', check: true)
  subproject('frida-swift')
endif

if get_option('frida_qml') \
    .disable_auto_if(is_cross_build) \
    .allowed()
  qt6_dep = dependency('qt6', modules: ['Core'], required: false)
  if get_option('frida_qml') \
      .disable_auto_if(not qt6_dep.found()) \
      .allowed()
    run_command(ensure_submodules, 'frida-qml', check: true)
    subproject('frida-qml')
  endif
endif

if get_option('frida_tools') \
    .disable_auto_if(is_cross_build) \
    .allowed()
  run_command(ensure_submodules, 'frida-tools', check: true)
  subproject('frida-tools')
endif


================================================
FILE: meson.options
================================================
option('frida_tools',
  type: 'feature',
  value: 'auto',
  description: 'Build CLI tools, like frida, frida-trace, etc.',
)

option('graft_tool',
  type: 'feature',
  value: 'auto',
  description: 'Build gum-graft tool',
)

option('gadget',
  type: 'feature',
  value: 'auto',
  description: 'Build frida-gadget',
)

option('server',
  type: 'feature',
  value: 'auto',
  description: 'Build frida-server',
)

option('portal',
  type: 'feature',
  value: 'disabled',
  description: 'Build frida-portal',
)

option('inject',
  type: 'feature',
  value: 'auto',
  description: 'Build frida-inject',
)

option('frida_clr',
  type: 'feature',
  value: 'disabled',
  description: 'Build .NET bindings',
)

option('frida_node',
  type: 'feature',
  value: 'disabled',
  description: 'Build Node.js bindings',
)

option('frida_python',
  type: 'feature',
  value: 'auto',
  description: 'Build Python bindings',
)

option('frida_swift',
  type: 'feature',
  value: 'disabled',
  description: 'Build Swift bindings',
)

option('frida_qml',
  type: 'feature',
  value: 'disabled',
  description: 'Build QML bindings',
)


================================================
FILE: tools/ensure-submodules.py
================================================
#!/usr/bin/env python3

import subprocess
import sys
from pathlib import Path

SOURCE_ROOT = Path(__file__).resolve().parent.parent
UPDATE_FLAGS = ["--init", "--depth", "1"]


def main(argv: list[str]):
    names = argv[1:]
    if not names:
        names = ["frida-gum", "frida-core"]
    paths_to_check = [Path("subprojects") / name for name in names]

    try:
        releng = SOURCE_ROOT / "releng"
        if not (releng / "meson" / "meson.py").exists():
            print(f"Fetching releng...", flush=True)
            run(["git", "submodule", "update", *UPDATE_FLAGS, releng.name], cwd=SOURCE_ROOT)
            run(["git", "submodule", "update", *UPDATE_FLAGS], cwd=releng)

        for relpath in paths_to_check:
            if not (SOURCE_ROOT / relpath / "meson.build").exists():
                print(f"Fetching {relpath.name}...", flush=True)
                run(["git", "submodule", "update", *UPDATE_FLAGS, relpath], cwd=SOURCE_ROOT)
    except Exception as e:
        print(e, file=sys.stderr)
        if isinstance(e, subprocess.CalledProcessError):
            for label, data in [("Output", e.output), ("Stderr", e.stderr)]:
                if data:
                    print(f"{label}:\n\t| " + "\n\t| ".join(data.strip().split("\n")), file=sys.stderr)
        sys.exit(1)


def run(argv: list[str], **kwargs) -> subprocess.CompletedProcess:
    return subprocess.run(argv, capture_output=True, encoding="utf-8", check=True, **kwargs)


if __name__ == "__main__":
    main(sys.argv)
Download .txt
gitextract_t62922vv/

├── .cirrus.yml
├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── actions/
│   │   ├── package-artifact-file/
│   │   │   └── action.yml
│   │   ├── package-artifact-files-as-sfx/
│   │   │   └── action.yml
│   │   ├── package-artifact-files-as-tarball/
│   │   │   └── action.yml
│   │   ├── package-ios-assets/
│   │   │   └── action.yml
│   │   ├── package-tvos-assets/
│   │   │   └── action.yml
│   │   ├── prepare-python-packages/
│   │   │   └── action.yml
│   │   ├── publish-debs/
│   │   │   └── action.yml
│   │   ├── setup-freebsd-env/
│   │   │   └── action.yml
│   │   ├── setup-linux-env/
│   │   │   └── action.yml
│   │   ├── setup-macos-env/
│   │   │   └── action.yml
│   │   └── setup-windows-env/
│   │       └── action.yml
│   ├── scripts/
│   │   ├── package-cirrus-ci-artifacts.sh
│   │   ├── rename-release-assets.sh
│   │   └── repo.py
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .gitmodules
├── BSDmakefile
├── CONTRIBUTING.md
├── COPYING
├── Makefile
├── README.md
├── configure
├── configure.bat
├── make.bat
├── meson.build
├── meson.options
└── tools/
    └── ensure-submodules.py
Download .txt
SYMBOL INDEX (17 symbols across 2 files)

FILE: .github/scripts/repo.py
  function main (line 30) | def main(argv: list[str]):
  function bump (line 62) | def bump():
  function bump_subproject (line 84) | def bump_subproject(name: str, repo: Path):
  function bump_releng (line 141) | def bump_releng(releng: Path):
  function bump_submodules (line 148) | def bump_submodules() -> list[str]:
  function tag (line 163) | def tag(version: str):
  function prepublish (line 174) | def prepublish(name: str, version: str, repo: Path):
  function backtag (line 197) | def backtag(version: str):
  function enumerate_projects_in_release_cycle (line 207) | def enumerate_projects_in_release_cycle() -> Iterator[tuple[str, Path]]:
  function enumerate_git_wraps_in_repo (line 212) | def enumerate_git_wraps_in_repo(repo: Path) -> Iterator[tuple[str, Confi...
  function assert_no_local_changes (line 225) | def assert_no_local_changes(repo: Path):
  function query_local_changes (line 229) | def query_local_changes(repo: Path) -> list[str]:
  function push_changes (line 236) | def push_changes(name: str, repo: Path):
  function ensure_remote_origin_writable (line 241) | def ensure_remote_origin_writable(name: str, repo: Path):
  function run (line 248) | def run(argv: list[str], **kwargs) -> subprocess.CompletedProcess:

FILE: tools/ensure-submodules.py
  function main (line 11) | def main(argv: list[str]):
  function run (line 37) | def run(argv: list[str], **kwargs) -> subprocess.CompletedProcess:
Condensed preview — 31 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (155K chars).
[
  {
    "path": ".cirrus.yml",
    "chars": 3910,
    "preview": "task:\n  name: freebsd-x86_64\n  freebsd_instance:\n    image_family: freebsd-15-0-amd64-ufs\n  environment:\n    S3_ACCESS_K"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 28,
    "preview": "/.github/workflows/ @oleavr\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 14,
    "preview": "github: frida\n"
  },
  {
    "path": ".github/actions/package-artifact-file/action.yml",
    "chars": 1073,
    "preview": "name: Package single-file artifact\ndescription: Downloads an artifact and uploads a compressed version of its file\ninput"
  },
  {
    "path": ".github/actions/package-artifact-files-as-sfx/action.yml",
    "chars": 645,
    "preview": "name: Package artifact files as SFX\ndescription: Downloads a specific artifact and uploads a packaged version of it\ninpu"
  },
  {
    "path": ".github/actions/package-artifact-files-as-tarball/action.yml",
    "chars": 668,
    "preview": "name: Package artifact files as tarball\ndescription: Downloads a specific artifact and uploads a packaged version of it\n"
  },
  {
    "path": ".github/actions/package-ios-assets/action.yml",
    "chars": 619,
    "preview": "name: Package iOS assets\ndescription: Downloads iOS assets and packages them\nruns:\n  using: composite\n  steps:\n    - nam"
  },
  {
    "path": ".github/actions/package-tvos-assets/action.yml",
    "chars": 573,
    "preview": "name: Package tvOS assets\ndescription: Downloads tvOS assets and packages them\nruns:\n  using: composite\n  steps:\n    - n"
  },
  {
    "path": ".github/actions/prepare-python-packages/action.yml",
    "chars": 1936,
    "preview": "name: Prepare Python packages for publishing\ndescription: Prepares Python source distribution and wheels to be published"
  },
  {
    "path": ".github/actions/publish-debs/action.yml",
    "chars": 2601,
    "preview": "name: Publish .deb packages\ndescription: Publishes already packaged debs\ninputs:\n  site:\n    required: true\n    path: Wh"
  },
  {
    "path": ".github/actions/setup-freebsd-env/action.yml",
    "chars": 1779,
    "preview": "name: Set up FreeBSD environment\ndescription: Set up everything needed to build and release things on FreeBSD\ninputs:\n  "
  },
  {
    "path": ".github/actions/setup-linux-env/action.yml",
    "chars": 1775,
    "preview": "name: Set up Linux environment\ndescription: Set up everything needed to build and release things on Linux\ninputs:\n  aws-"
  },
  {
    "path": ".github/actions/setup-macos-env/action.yml",
    "chars": 3602,
    "preview": "name: Set up macOS environment\ndescription: Set up everything needed to build and release things on macOS\ninputs:\n  cert"
  },
  {
    "path": ".github/actions/setup-windows-env/action.yml",
    "chars": 2133,
    "preview": "name: Set up Windows environment\ndescription: Set up everything needed to build and release things on Windows\ninputs:\n  "
  },
  {
    "path": ".github/scripts/package-cirrus-ci-artifacts.sh",
    "chars": 2741,
    "preview": "#!/bin/bash\n\nsha=$1\nif [ -z \"$sha\" ]; then\n  echo \"Usage: $0 <sha>\" > /dev/stderr\n  exit 1\nfi\n\nset -e\n\nbuild_id=\"\"\nwhile"
  },
  {
    "path": ".github/scripts/rename-release-assets.sh",
    "chars": 665,
    "preview": "#!/bin/bash\n\nif [ -z \"$FRIDA_VERSION\" ]; then\n  echo \"FRIDA_VERSION must be set\" > /dev/stderr\n  exit 1\nfi\n\nset -e\n\ncd b"
  },
  {
    "path": ".github/scripts/repo.py",
    "chars": 8893,
    "preview": "#!/usr/bin/env python3\n\nimport argparse\nfrom configparser import ConfigParser\nfrom pathlib import Path\nimport subprocess"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 77993,
    "preview": "name: CI\n\non: push\n\nenv:\n  FRIDA_COMPILER_MOD: subprojects/frida-core/src/compiler/go.mod\n  ANDROID_NDK_VERSION: r29\n\njo"
  },
  {
    "path": ".gitignore",
    "chars": 138,
    "preview": "/build/\n/deps/\n/releng/__pycache__/\n/releng/modules/frida-gadget-ios/*.dylib\n/releng/modules/frida-gadget-ios/node_modul"
  },
  {
    "path": ".gitmodules",
    "chars": 999,
    "preview": "[submodule \"frida-gum\"]\n\tpath = subprojects/frida-gum\n\turl = https://github.com/frida/frida-gum.git\n[submodule \"frida-co"
  },
  {
    "path": "BSDmakefile",
    "chars": 72,
    "preview": "all: .DEFAULT\n\n.DEFAULT:\n\t@gmake ${.MAKEFLAGS} ${.TARGETS}\n\n.PHONY: all\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 23070,
    "preview": "# Intro\n\nFrida is composed of many sub-projects and its code spans across many\ndifferent languages, such as C, C++, Vala"
  },
  {
    "path": "COPYING",
    "chars": 2416,
    "preview": "              wxWindows Library Licence, Version 3.1\n              ======================================\n\nCopyright (c)"
  },
  {
    "path": "Makefile",
    "chars": 400,
    "preview": "PYTHON ?= $(shell which python3 >/dev/null && echo python3 || echo python)\n\nall $(MAKECMDGOALS):\n\t@$(PYTHON) \\\n\t\t-c \"imp"
  },
  {
    "path": "README.md",
    "chars": 1908,
    "preview": "# Frida\n\nDynamic instrumentation toolkit for developers, reverse-engineers, and security\nresearchers. Learn more at [fri"
  },
  {
    "path": "configure",
    "chars": 365,
    "preview": "#!/bin/sh\n\n[ -z \"$PYTHON\" ] && PYTHON=$(which python3 >/dev/null && echo python3 || echo python)\n\ncd $(dirname $0)\nsrcro"
  },
  {
    "path": "configure.bat",
    "chars": 563,
    "preview": "@setlocal\r\n@echo off\r\nrem:: Based on: https://github.com/microsoft/terminal/issues/217#issuecomment-737594785\r\ngoto :_st"
  },
  {
    "path": "make.bat",
    "chars": 573,
    "preview": "@setlocal\r\n@echo off\r\nrem:: Based on: https://github.com/microsoft/terminal/issues/217#issuecomment-737594785\r\ngoto :_st"
  },
  {
    "path": "meson.build",
    "chars": 2794,
    "preview": "project('frida', 'c',\n  version: run_command('releng' / 'frida_version.py', check: true).stdout().strip(),\n  meson_versi"
  },
  {
    "path": "meson.options",
    "chars": 1112,
    "preview": "option('frida_tools',\n  type: 'feature',\n  value: 'auto',\n  description: 'Build CLI tools, like frida, frida-trace, etc."
  },
  {
    "path": "tools/ensure-submodules.py",
    "chars": 1503,
    "preview": "#!/usr/bin/env python3\n\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nSOURCE_ROOT = Path(__file__).resolve().pa"
  }
]

About this extraction

This page contains the full source code of the frida/frida GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 31 files (144.1 KB), approximately 40.3k tokens, and a symbol index with 17 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!